ClickerMonkey / vuex-typescript-interface

Adding magical type safety to Vuex without additional code
MIT License
44 stars 4 forks source link

Type inference with generics #11

Open gregoirechauvet opened 4 years ago

gregoirechauvet commented 4 years ago

Hey, thanks for your awesome lib!

When using generics in mutations (and actions too), lookup types are not inferred as accurately as they could be.

Here is my store definition:

import { Module, MutationTree } from 'vuex-typescript-interface';

interface Filters {
  search: string;
  toggleThing: boolean;
}

interface FiltersModule {
  filters: Filters,
  UPDATE_FILTERS<T extends keyof Filters>(payload: { key: T, value: Filters[T] }): void;
}

const mutations: MutationTree<FiltersModule> = {
  UPDATE_FILTERS: (state, {key, value}) => {
    state.filters = {
      ...state.filters,
      [key]: value
    };
  }
}

const module: Module<FiltersModule, RootState> = {
  state: {
    filters: {
      search: '',
      toggleThing: false
    }
  },
  mutations: mutations
}

export default module;

And when I use the mutation in a component:

store.commit('UPDATE_FILTERS', { key: 'search', value: 'foo' }) // Working, as expected
store.commit('UPDATE_FILTERS', { key: 'search', value: 4 }) // Fail, of course
store.commit('UPDATE_FILTERS', { key: 'search', value: true }) // Does not fail even if it should 😞

It seems that the union of all possible types is inferred, so here value can be string or boolean, instead of being specific to the key used.

Screenshot from 2019-08-27 17-29-59

But TypeScript should be able to handle this situation correctly. It does work with a standard function:

interface Filters {
  search: string;
  toggleThing: boolean;
}

function test<T extends keyof Filters>(payload: { key: T, value: Filters[T] }): void {
  // Do nothing
}

test({ key: 'search', value: 'foo' }) // Ok
test({ key: 'search', value: 4 }) // Fail
test({ key: 'search', value: true }) // Fail too

Is it something that can be improved? :slightly_smiling_face:

ClickerMonkey commented 3 years ago

I'll look into it as I work on the new version for Vuex 4