简体   繁体   中英

Why can't these generic type be inferred?

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]
      }
    }
  }
})

Codesandbox IDE snapshot

Further code info can be found here: https://github.com/js-cosmos/vuex-light/issues/11

Thanks for your help:D.


Update:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM