Skip to content

Instantly share code, notes, and snippets.

@lmiller1990
Last active September 27, 2023 08:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lmiller1990/9ef32df8fb401e5f0482692ae974e6e0 to your computer and use it in GitHub Desktop.
Save lmiller1990/9ef32df8fb401e5f0482692ae974e6e0 to your computer and use it in GitHub Desktop.
typesafe-store.ts
import { reactive } from 'vue';
type Method = (...args: any[]) => any
type StoreWithState<S extends StateTree> = {
state: S
}
type StoreWithActions<A> = {
[k in keyof A]: A[k] extends (...args: infer P) => infer R
? (...args: P) => R
: never
}
type Store<
S extends StateTree,
A extends Record<string, Method>
> = StoreWithState<S> & StoreWithActions<A>;
export type StateTree = Record<string | number | symbol, any>;
function defineStore<
S extends StateTree,
A extends Record<string, Method>
>(options: {
state: S,
actions: A & ThisType<A & StoreWithState<S>>
}) {
const initialStore = {
state: options.state
}
return function useStore(): Store<S, A> {
const wrappedActions: StoreWithActions<A> = {} as StoreWithActions<A>
const actions = (options.actions || {}) as A
for (const actionName in actions) {
wrappedActions[actionName] = function(...args: any[]) {
return actions[actionName].apply(store, args)
} as StoreWithActions<A>[typeof actionName]
}
const store: Store<S, A> = reactive({
...initialStore,
...wrappedActions
}) as Store<S, A>
return store
}
}
export const useMainStore = defineStore({
state: {
counter: 0,
},
actions: {
inc(val: number = 1) {
this.state.counter += val
},
},
});
const main = useMainStore()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment