Vuex map functions with TypeScript

2.3k Views Asked by At

I have an interface for my state (game namespace) that looks like the following:

interface GameState {
  selectedTab: number;
  timeout: number;
  snackbar: boolean;
  text: string;
  doneTasks: number;
  taskTypes: TaskType[];
  stages: Stage[];
}

i'm trying to map 'taskTypes' inside a component using mapState like this:

...mapState('game', {
  taskTypes: (state: GameState): TaskType[] => state.taskTypes,
}),

i get the following error "Argument of type '{ taskTypes: (state: GameState) => TaskType[]; }' is not assignable to parameter of type 'string[]'."

when defining the "state" parameter type of the function as 'any' it compiles and works, so i tried to "debug" that function in devtools and see what the "state" param holds, and it holds my game state, which has the exact props that are defined in the GameState interface.

does anyone know why ts won't let me define the "state" param with it's correct type (that is the whole purpose of defining interfaces in the first place and avoiding 'any')

this also doesn't work:

...mapState({
  taskTypes: ({ state }: Store<GameState>): TaskType[] => state.taskTypes,
}),

has anyone ever came across such problem? any ideas how of the best practice way to deal with it?

goes same for mapActions etc...

UPDATE i try to give better typings to my store and modules. right now they are the following: main store:

export default new Store<RootState>({
  modules: {
    users,
    game,
  },
});

with RootState being:

export interface RootState {
  game: GameState;
  users: UserState;
}

game module:

type GameActionContext = ActionContext<GameState, RootState>;

const gameModule: Module<GameState, RootState> = {
  namespaced: true,
  state: {
    selectedTab: 0,
    timeout: 5000,
    snackbar: false,
    text: '',
    doneTasks: 0,
    taskTypes: [...],
    stages: [...],
  } as GameState,
  mutations: {/*Mutations*/} as MutationTree<GameState>,
  actions: {/*Actions*/} as ActionTree<GameState, RootState>,
};

export default gameModule;

I'm trying to map a state obejct the following way:

...mapState({
      taskTypes: (state): TaskType[] => state.GameState.taskTypes
})

but i'm receiving the error:

(parameter) state: unknown - Object is of type 'unknown'

4

There are 4 best solutions below

0
On BEST ANSWER

Posting the best solution i came up with so far (as far for least code and typed code):

...mapState({
  taskTypes: (state): TaskType[] => (state as RootState).game.taskTypes,
}),

because state will always be equal to RootState that casting will always stay the same for every mapState. can't say im completely happy with this solution but for now it seems to be the best one (doesn't require any extra interfaces / long lines of casting types)

2
On

I've generally had to cast the objects as unknown and then to the actual type.

You can try this approach, created a generic type like this that basically will make the values available as functions:

export type Computed<T> = {
    [K in keyof T]: () => T[K];
};

Now you can use this in a component like this:

...((mapState('auth', ['taskTypes']) as unknown) as Pick<Computed<GameState>, 'taskTypes'>)

Now this.taskTypes should be be typed.

0
On

You can pass a generic type to mapState.

...mapState<GameState>('game', {
  taskTypes: (state: GameState): TaskType[] => state.taskTypes,
}),
0
On

I got the same error message on mapGetters the way I have managed to type it inside a Vue2 component correctly is like this:

...(mapGetters("module", ['someGetter']) as {someGetter: () => boolean}),
...(mapState("appModule", ["appIsLoading"]) as {appIsLoading: () => boolean;}),
...(mapMutations("appModule", ["SET_APP_IS_LOADING"]) as {SET_APP_IS_LOADING: (payload: boolean) => void;}),
...(mapActions("appModule", ["extendLockTime"]) as {extendLockTime: () => void;}),

With this type hinting and type checking is correct inside the component.