Skip to content

Instantly share code, notes, and snippets.

@Tobiaqs
Last active September 22, 2023 08:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tobiaqs/f1e5059dda45bf360744cf3b64568da6 to your computer and use it in GitHub Desktop.
Save Tobiaqs/f1e5059dda45bf360744cf3b64568da6 to your computer and use it in GitHub Desktop.
Vuex 4 store with fully typed mapState (supports modules and actions)
/// store.ts
import { InjectionKey } from 'vue'
import { createStore, mapState as baseMapState, Store, useStore as baseUseStore } from 'vuex'
const modules = {
appDataModule,
}
const state = {
settings: <Settings | null>null,
users: <User[]>[],
}
const mutations = {
loadSettings(state, settings: Settings) {
state.settings = settings
},
loadUsers(state, users: User[]) {
state.users = users
},
}
const actions = {
fetchSettings: (context) => {
axios
.get(import.meta.env.VITE_BACKEND_API_URL + 'base/settings/')
.then((response) => {
context.commit('loadSettings', response.data)
})
.catch(() => {
alert('error')
})
},
fetchUsers: (context) => {
actionPromises.fetchUsers = axios
.get(import.meta.env.VITE_BACKEND_API_URL + 'base/users/')
.then((response) => {
context.commit('loadUsers', response.data)
})
.catch(() => {
alert('error')
})
},
}
export const store = createStore({
state,
mutations,
actions,
modules,
})
// Utility type to extract string literals from modules, to be used for auto suggestions for store.dispatch
type ActionPaths<T, Prefix extends string = ''> = {
[Key in keyof T]: T[Key] extends { actions: any }
? `${Prefix & string}${Key & string}/${Extract<keyof T[Key]['actions'], string>}` | ActionPaths<T[Key]['actions'], `${Prefix & string}${Key & string}/`>
: never
}[keyof T]
type ModuleActions = keyof typeof actions | ActionPaths<typeof modules>
type StoreWithTypedDispatch = Store<State> & { dispatch: (str: ModuleActions) => ReturnType<typeof baseUseStore>['dispatch'] }
export const key: InjectionKey<StoreWithTypedDispatch> = Symbol()
// Typed useStore
export function useStore() {
return baseUseStore(key) as StoreWithTypedDispatch
}
// Typed version of mapState
export const mapState = <T = keyof State>(keys: readonly T[]) => {
const ms = baseMapState(keys.slice(0) as string[])
const d = {}
for (const key of keys) {
d[key as string] = ms[key as string]
}
return d as { [Property in keyof State & T]: () => State[Property] }
}
/// main.ts
import { key, store } from './store'
...
const app = createApp(App).use(store, key).use(router)
/// Component.vue
<template></template>
<script lang="ts" setup>
import { mapState } from '@/store'
defineComponent({
computed: {
...mapState(['users', 'settings'] as const),
}
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment