Created
July 14, 2020 07:24
-
-
Save inad9300/d3da1744974d1ce4b0c3c84f4e287e42 to your computer and use it in GitHub Desktop.
Type-safe Vuex store.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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