Question3r
Question3r

Reputation: 3742

How to setup a store module using Vue3 with Vuex4 and TypeScript?

I want to get into Vue3 / Vuex4 using TypeScript but unfortunately the docs didn’t help much

https://next.vuex.vuejs.org/guide/typescript-support.html

As a beginner, I started with a simple issue tracking store module. Inside my issues module I created a index.ts file taking in the “global store instance” and registers itself

import {
  ActionContext,
  ActionTree,
  GetterTree,
  MutationTree,
  Store,
} from "vuex";

export default (store: Store<{}>): void => {
  store.registerModule("issues", {
    namespaced: true,
    state,
    getters,
    actions,
    mutations,
  });
};

export type Issue = {
  title: string;
  isOpen: boolean;
  createdAt: Date;
};

export type State = {
  issues: Issue[];
};

export type Getters = {
  issues(state: State): Issue[];
};

export type ActionAugments = Omit<ActionContext<State, State>, "commit"> & {
  commit<K extends keyof Mutations>(
    key: K,
    payload: Parameters<Mutations[K]>[1]
  ): ReturnType<Mutations[K]>;
};

export enum ActionType {
  SubmitIssue = "SubmitIssue",
}

export type Actions = {
  [ActionType.SubmitIssue](
    context: ActionAugments,
    submitIssuePayload: SubmitIssuePayload
  ): void;
};

export type SubmitIssuePayload = {
  issueTitle: string;
};

export enum MutationType {
  SubmitIssue = "SUBMIT_ISSUE",
}

export type Mutations = {
  [MutationType.SubmitIssue](state: State, payload: SubmitIssuePayload): void;
};

const state: State = {
  issues: [],
};

const getters: GetterTree<State, State> & Getters = {
  issues: function (state: State): Issue[] {
    return state.issues;
  },
};

const actions: ActionTree<State, State> & Actions = {
  [ActionType.SubmitIssue]: function (
    { commit }: ActionAugments,
    { issueTitle }: SubmitIssuePayload
  ): void {
    commit(MutationType.SubmitIssue, { issueTitle });
  },
};

const mutations: MutationTree<State> & Mutations = {
  [MutationType.SubmitIssue]: function (
    state: State,
    { issueTitle }: SubmitIssuePayload
  ): void {
    const issue: Issue = {
      title: issueTitle,
      isOpen: true,
      createdAt: new Date(),
    };

    state.issues.push(issue);
  },
};

Currently I’m stuck with the setup for some reasons:

.

No overload matches this call.
  Overload 1 of 2, '(path: string, module: Module<State, {}>, options?: ModuleOptions | undefined): void', gave the following error.
    Type 'GetterTree<State, State> & Getters' is not assignable to type 'GetterTree<State, {}> | undefined'.
      Type 'GetterTree<State, State> & Getters' is not assignable to type 'GetterTree<State, {}>'.
        'string' index signatures are incompatible.
          Type 'Getter<State, State>' is not assignable to type 'Getter<State, {}>'.
            Property 'issues' is missing in type '{}' but required in type 'State'.
  Overload 2 of 2, '(path: string[], module: Module<State, {}>, options?: ModuleOptions | undefined): void', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'string[]'.ts(2769)
store.ts(26, 3): 'issues' is declared here.
index.d.ts(132, 3): The expected type comes from property 'getters' which is declared here on type 'Module<State, {}>'

which makes sense, because the global store instance is of type Store<{}> and my module seems to expect Store<State>

How would I fix this?

Thanks for help!

Upvotes: 1

Views: 1577

Answers (1)

Estus Flask
Estus Flask

Reputation: 222309

The problem is that module state type was specified in places where root state is expected, while they aren't same.

It's preferable to keep root state type as separate type and import instead of hardcoding it like {} because it can change with time.

It should be store: Store<RootState>, GetterTree<State, RootState>, etc. Where root state generic parameter affects the type of rootState in Vuex context.

Upvotes: 1

Related Questions