Skip to content

Instantly share code, notes, and snippets.

@inad9300
Created July 14, 2020 07:24
Show Gist options
  • Save inad9300/d3da1744974d1ce4b0c3c84f4e287e42 to your computer and use it in GitHub Desktop.
Save inad9300/d3da1744974d1ce4b0c3c84f4e287e42 to your computer and use it in GitHub Desktop.
Type-safe Vuex store.
import * as Vuex from 'vuex';
import type * as Vue from 'vue';
type CommitOptions = Omit<Vuex.CommitOptions, 'root'>;
type DispatchOptions = Omit<Vuex.DispatchOptions, 'root'>;
type ThenArg<T> = T extends PromiseLike<infer U> ? U : T;
/**
* Create a type-safe Vuex store.
*/
export function createStore<
State,
MutationTree extends Record<string, (state: State, payload?: any) => void>,
MutationName extends keyof MutationTree,
ActionTree extends Record<string, (context: { state: State }, payload?: any) => void | Promise<any>>,
ActionName extends keyof ActionTree,
GetterTree extends Record<string, (state: State) => any>
>(
options: { state: State, mutations: MutationTree, actions?: ActionTree, getters?: GetterTree }
) {
return new Vuex.Store<State>(options as any) as {
readonly state: State;
readonly getters: {
[G in keyof GetterTree]: ReturnType<GetterTree[G]>;
};
replaceState(state: State): void;
subscribe<M extends MutationName>(
cb: (mutation: { type: M, payload: Parameters<MutationTree[M]>[1] }, state: State) => void,
options?: Vuex.SubscribeOptions
): () => void;
subscribeAction<A extends ActionName>(
cbs: Vuex.SubscribeActionOptions<{ type: A, payload: Parameters<ActionTree[A]>[1] }, State>,
options?: Vuex.SubscribeOptions
): () => void;
watch<T>(
getter: (state: State/*, getters: Getters*/) => T,
cb: (value: T, oldValue: T) => void,
options?: Vue.WatchOptions
): () => void;
commit<M extends MutationName>(
mutation: M,
...payloadAndOptions:
Parameters<MutationTree[M]>[1] extends undefined
? [undefined?, CommitOptions?]
: [Parameters<MutationTree[M]>[1], CommitOptions?]
): void;
dispatch<A extends ActionName>(
action: ActionName,
...payloadAndOptions:
Parameters<ActionTree[A]>[1] extends undefined
? [undefined?, DispatchOptions?]
: [Parameters<ActionTree[A]>[1], DispatchOptions?]
): Promise<ReturnType<ActionTree[A]> extends undefined ? void : ThenArg<ReturnType<ActionTree[A]>>>;
};
}
// Usage
const store = createStore({
state: {
count: 0
},
mutations: {
INC(state) {
state.count++;
},
DEC(state) {
state.count--;
},
},
actions: {
incrementCount() {
store.commit('INC');
},
decrementCount() {
store.commit('DEC');
}
},
getters: {
oppositeCount(state) {
return -state.count;
},
zero(state) {
// This kind of recursion breaks TypeScript's type inference :/ ...
// Providing `getters` as an argument to all getters directly from the types
// of the function declaration above makes TypeScript equally unhappy :( ...
// return state + store.getters.oppositeCount;
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment