I am trying to figure out how to type vuex modules in a vue 3 typescript project. The official documentation is lacking in this area.
Assume I have a project like this:
import { createStore, useStore as baseUseStore, Store } from 'vuex';
import { InjectionKey } from 'vue';
interface FruitState {
apple: boolean,
peach: boolean,
plum: boolean
}
const FruitModule = {
namespaced: true,
state: (): FruitState => ({
apple: true,
peach: false,
plum: true
}),
mutations: {},
action: {}
}
export interface State {
foo: string;
}
export const key: InjectionKey<Store<State>> = Symbol();
export const store = createStore<State>({
modules: {
fruit: fruitModule
},
state: {foo: 'foo'},
mutations: {
changeFoo(state: State, payload: string){
state.foo = payload
}
},
actions: {
setFooToBar({commit}){
commit('changeFoo', 'bar')
}}
})
export function useStoreTyped() {
return baseUseStore(key);
}
... then later in a component:
const apple = computed(() => store.state.fruit.apple);
When I try to access apple it does not work because it throws error
Property 'fruit' does not exist on type 'State'
Now IF I do something like this:
import { createStore, useStore as baseUseStore, Store } from 'vuex';
import { InjectionKey } from 'vue';
interface FruitState {
apple: boolean,
peach: boolean,
plum: boolean
}
const FruitModule = {
namespaced: true,
state: (): FruitState => ({
apple: true,
peach: false,
plum: true,
}),
mutations: {},
action: {}
}
export interface State {
foo: string;
fruit?: FruitState;
}
export const key: InjectionKey<Store<State>> = Symbol();
export const store = createStore<State>({
modules: {
fruit: fruitModule
},
state: {foo: 'foo'},
mutations: {
changeFoo(state: State, payload: string){
state.foo = payload
}
},
actions: {
setFooToBar({commit}){
commit('changeFoo', 'bar')
}}
})
export function useStoreTyped() {
return baseUseStore(key);
}
And try again, the error changes to Object is possibly 'undefined'
It will allow me let me access the fruit module if I use the optional chaining ?.
As in const apple = computed(() => store.state.fruit?.apple);
But this doesn't seem acceptable to me since I know that fruit.apple is actually not undefined.
What's the correct way to include a module in your state types for vuex?
fruit
is declared as optional in theState
interface, so it's therefore possibly undefined, and it's access must always be checked. Making it optional implies thatstore.state.fruit
might be (re)-assigned toundefined
ornull
(e.g., in between calls). If you knowfruit
will always be defined, then don't make it optional inState
. Why have you made it optional to begin with? – Wilkefruit
prop in the state object that is constructed from thecreateStore
function, this is what thenamespaced: true
option is for on the module. Making a fruit property onState
interface was my attempt to let TS know that the module will eventually exist. And making it optional was so that I didn't have to include it in the initial top level state, because it is supposed to be set in the module. – ChandauseState<State>()
. – Apoenzyme