I'm writing a Vuex store for Vue 3, and the following issue has been bother me recently.
Please consider following minimal reproducible example:
interface Options<State, G extends Getters<State>> {
state: State
getters: G,
mutations: {
[P: string]: (state: State, getters: G) => void
}
}
interface Getters<State> {
[P: string]: (state: State) => any
}
function createStore<State>(options: Options<State, Getters<State>>) {
return options
}
createStore({
state: {
count: 0
},
getters: {
isOdd: (state) => state.count % 2 === 1
},
mutations: {
incrementIfOdd (state, getters) {
if (getters.isOdd) {
// ^ a this cannot be inferred to Boolean here.
++state.count
// ^ ok, it is number.[enter image description here][1]
}
}
}
})
Further code info can be found here: https://github.com/js-cosmos/vuex-light/issues/11
Thanks for your help:D.
I try to remove wrapped option and it works!
To simplify the example code:
// This works function spreadOptionsFn< State extends Record<any, any>, Getters extends Record<any, ({ state }: { state: State }) => any>, Mutations extends Record<any, ({ state, getters }: { state: State; getters: Getters }) => any> >(state: State, getters: Getters, mutations: Mutations) {} spreadOptionsFn( { stateKey: 'state' }, { getterKey: ({ state }) => state.stateKey }, { mutationKey: ({ state, getters }) => [state.stateKey, getters.getterKey] }, ) // But not this function wrappedOptionsFn< State extends Record<any, any>, Getters extends Record<any, ({ state }: { state: State }) => any>, Mutations extends Record<any, ({ state, getters }: { state: State; getters: Getters }) => any> >({ state, getters, mutations }: { state: State; getters: Getters; mutations: Mutations }) {} wrappedOptionsFn({ state: { stateKey: 'state' }, getters: { getterKey: ({ state }) => state.stateKey }, mutations: { mutationKey: ({ state, getters }) => [state.stateKey, getters.getterKey] }, })
CodeSandbox screenshot for type inference
Update 2:
I missing something again, It finally be fixed with the following code:
interface Options<
State extends Record<any, any>,
GetterKeys extends string,
MutationKeys extends string,
> {
state: State,
getters: {
[P in GetterKeys]: ({ state }: { state: State }) => any
},
mutations: {
[P in MutationKeys]: ({ state, getters }: { state: State; getters: {
[P in GetterKeys]: ({ state }: { state: State }) => any
} }) => any
},
}
function wrappedOptionsFn<
State extends Record<any, any>,
GetterKeys extends string,
MutationKeys extends string,
>(options: Options<State, GetterKeys, MutationKeys>) {
}
wrappedOptionsFn({
state: { stateKey: 'state' },
getters: { getterKey: ({ state }) => state.stateKey },
mutations: { mutationKey: ({ state, getters }) => [state.stateKey, getters.getterKey] },
})
This is because you're using P: string for your mutations and getters, so we don't know what the specific keys are, and therefore their types can be inferred.
You should use extra generic parameters for P here - for the MutationKeys and GetterKeys, which can then be inferred correctly:
interface Options<
State,
MutationKeys extends string,
G extends Getters<State, string>
> {
state: State;
getters: G;
mutations: {
[P in MutationKeys]: (state: State, getters: G) => void;
};
}
type Getters<State, Keys extends string> = {
[key in Keys]: (state: State) => any;
};
function createStore<
State,
MutationKeys extends string,
GetterKeys extends string
>(options: Options<State, MutationKeys, Getters<State, GetterKeys>>) {
return options;
}
createStore({
state: {
count: 0
},
getters: {
isOdd: (state) => state.count % 2 === 1
},
mutations: {
incrementIfOdd(state, getters) {
if (getters.isOdd(state)) {
++state.count;
}
}
}
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.