A simple way to get static typing, static code analysis and intellisense with Vuex library.
npm install vuex-typescriptjs
import { ActionContext, Store } from "vuex";
import { getStoreAccessors } from "vuex-typescript";
import { State as RootState } from "../state";
import { BasketState, Product, ProductInBasket } from "./basketState";
// This part is a vanilla Vuex module, nothing fancy:
type BasketContext = ActionContext;
export const basket = {
namespaced: true,
state: {
items: [],
totalAmount: 0,
},
getters: {
getProductNames(state: BasketState) {
return state.items.map((item) => item.product.name);
},
...
},
mutations: {
appendItem(state: BasketState, item: { product: Product; atTheEnd: boolean }) {
state.items.push({ product: item.product, isSelected: false });
},
...
},
actions: {
async updateTotalAmount(context: BasketContext, discount: number): Promise {
const totalBeforeDiscount = readTotalAmountWithoutDiscount(context);
const totalAfterDiscount = await callServer(totalBeforeDiscount, discount);
commitSetTotalAmount(context, totalAfterDiscount);
},
...
},
};
// This is where the vuex-typescript specific stuff begins:
//
// We want to expose static functions which will call get, dispatch or commit method
// on a store instance taking correct type of payload (or getter signature).
// Instead of writing these "store accessor" functions by hand, we use set of higher-order
// functions provided by vuex-typescript. These functions will produce statically typed
// functions which we want. Note that no type annotation is required at this point.
// Types of arguments are inferred from signature of vanilla vuex handlers defined above:
const { commit, read, dispatch } =
getStoreAccessors("basket"); // We pass namespace here, if we make the module namespaced: true.
export const readProductNames = read(basket.getters.getProductNames);
export const dispatchUpdateTotalAmount = dispatch(basket.actions.updateTotalAmount);
export const commitAppendItem = commit(basket.mutations.appendItem);
`
And then in your Vue component:
`js
import * as basket from "./store/basket"; // Or better import specific accessor to be explicit about what you use
...
getterResult = basket.readProductNames(this.$store); // This returns Product[]
basket.dispatchUpdateTotalAmount(this.$store, 0.5); // This accepts number (discount) - you'd normally use an object as arguments. Returns promise.
basket.commitAppendItem(this.$store, newItem); // This will give compilation error if you don't pass { product: Product; atTheEnd: boolean } in
`
Vuex version compatibility
For Vuex 2.x use newest vuex-typescript 2.x
For Vuex 3.x use newest vuex-typescript 3.x
This library has explicit dependency on Vuex.
A new version of vuex-typescript is released following each major release of Vuex. This way breaking API changes introduced into Vuex are guaranteed to be followed up and tested in vuex-typescript.
Functions or objects
This lib is deliberately designed with functions rather than classes. This does not stop you from grouping accessors into objects.
Note however that this makes little sense as the accessors are loosely or not related to each other.
Importing and using functions rather than objects makes it explicit which accessor you actually use rather than
stating which accessor in an object you may be using.
If you wish to define your vuex handlers as class members then you must decorate these methods with @Handler
decorator exported from this library as shown in this test.
More
https://github.com/mrcrowl/vuex-typex also uses higher-order functions but takes a few steps more: there the store is implicitly built while defining accessors for each handler (in contrast here we create store with standard Vuex options and then wrap handlers into accessors). It is also not required to pass $store as argument to accessors. Definitely worth checking out.
Contributing
`
npm run build
npm run build-watch
npm test
npm run test-watch
npm run test-debug
npm run coverage
``