Vuex4 does not seem to support direct syntax hints for commit and Dispatch

Therefore, under the condition that the store of the project is based on vuex modules, the project is reformed

Create a project

Use vue-cli to create the project, select TS and vuex

So I’m going to change it to modules

Let’s modify the file. The initial structure of vuex looks something like this

// store/index.ts

import { createStore } from 'vuex'
import modules from './modules'
export default createStore({
  modules: modules,
})

/ / main. In ts

import store from './store'
createApp(App).use(store)

// store/modules/index.ts

import user from "./user";
const modules = {
  user,
}
export default modules

// store/modules/user.ts

import { ActionContext } from "vuex";
export interface State { // Define the state type of the module
  name: string;
  token: string;
}
export const _state = {
  name: "".token: ""};export const mutations = {
  setName(state: State, params: string) {
    state.name = params;
  },
  setToken(state: State, params: string){ state.token = params; }};export const actions = {
  login({ commit }: ActionContext<State, any>) { // RootState is not exported, so use any instead
    commit("setToken"."token"); }};export default {
  namespaced: true.state: _state,
  mutations,
  actions,
};

Copy the code

Define the type of

With that done, let’s define the RootState in the store/modules/index.ts file to be used elsewhere

// Add the following code to store/modules/index.ts
import user, { State as UserState} from "./user";
import other, { State as OtherState} from "./other"; // If there are other modules, write this

export interface RootState {
  user: UserState,
  other: OtherState // If there are other modules, write this
}
Copy the code

tostore.stateType inference

We take modules and return to RootState as the type of state and define the key value to make the project use the store

// store/index.ts

import { createStore, Store, useStore as baseUseStore } from "vuex";
import modules, { RootState } from './modules'
import { InjectionKey } from 'vue'

export default createStore<RootState>({ // This is an inference to make the store reference the value directly in the code
  modules: modules,
});

// Define key to be used in main.ts
export const key: InjectionKey<Store<RootState>> = Symbol(a)export function useStore() {
  return baseUseStore(key)
}


// main.ts
import store, {key} from "./store";
createApp(App).use(store, key).use(router).mount("#app");

Copy the code

To obtain vuex data from the routing file, add the inferred import store from “@/store” using createStore

; console.log(store.state.user.name)

At this point we can happily refer to the exported useStore in the vue file, using the state syntax inference.

Why use the exported useStore instead of referencing vuex’s directly, in order to have less code that you might otherwise need to write

import { useStore } from 'vuex'
import { key } from '@/store'

setup() {
    const store = useStore(key)
    console.log(store.state.user.name)
}
Copy the code

Extrapolate to modulesmutationsactionsThe value of the

Create types. Ts under store

The code here draws lessons from the ARTICLE of SSH big guy accompany yu Yu stream together, achieve Vuex unlimited level type inference. I’ve been tinkering with it

import { Store as BaseStore, CommitOptions, DispatchOptions } from 'vuex'
import modules, { RootState } from './modules'

type RootType = typeof modules  // Infer the modules type

type AddPrefix<Prefix, Keys> = `${Prefix & string}/${Keys & string}`

type Split<S extends string, D extends string> = string extends S
  ? any[]
  : S extends ' '
  ? []
  : S extends `${infer T}${D}${infer U}`
  ? [T, ...Split<U, D>]
  : [S]

type GetMutations<Module> = Module extends { mutations: infer M } ? M : never
type GetActions<Module> = Module extends { actions: infer M } ? M : never

type GetModuleMutationKeys<Module, Key> = AddPrefix<Key, keyof GetMutations<Module>>
type GetModuleActionsKeys<Module, Key> = AddPrefix<Key, keyof GetActions<Module>>

type GetModulesMutationKeys<Modules> = {
  [K in keyof Modules]: GetModuleMutationKeys<Modules[K], K>
}[keyof Modules]

type GetModulesActionsKeys<Modules> = {
  [K in keyof Modules]: GetModuleActionsKeys<Modules[K], K>
}[keyof Modules]

type Commit = GetModulesMutationKeys<RootType>
type CommitFn = {
  Split
      
        = Split
       
         = Split
        
          = Split
        ,>
       ,>
      ,>
  [F in Commit]: RootType[Split<F, '/'> [0]] ['mutations'][Split<F, '/'> [1]]}type Dispatch = GetModulesActionsKeys<RootType>
type DispatchFn = {
  [F in Dispatch]: RootType[Split<F, '/'> [0]] ['actions'][Split<F, '/'> [1]]}export type Store = Omit<
  BaseStore<RootState>,
  // 'getters' |
  'commit' | 'dispatch'
> & {
  commit<K extends keyof CommitFn, P extends Parameters<CommitFn[K]>[1]>( key: K, payload? : P, options? : CommitOptions ): ReturnType<CommitFn[K]> } & { dispatch<Kextendskeyof DispatchFn>( key: K, payload? : Parameters<DispatchFn[K]>[1], options? : DispatchOptions ): ReturnType<DispatchFn[K]>/ /} and {
  // getters: {
  // [K in keyof Getters]: ReturnType
      [k]>
  / /}
}
Copy the code

Modify store/index.ts to force the vuex type to be your own modified type

import { createStore, Store, useStore as baseUseStore } from "vuex";
import modules, { RootState } from './modules'
import { InjectionKey } from 'vue'
import { Store as Store1 } from './types'

export default createStore<RootState>({
  modules: modules,
});  as Store1

// Define key to be used in main.ts
export const key: InjectionKey<Store<RootState>> = Symbol(a)export function useStore() {
  return baseUseStore(key)  as Store1
}
Copy the code

The code was a little ugly, but it worked, and it worked for a short time

The code still has very big flaw, wait for me ts big method after learning ~ 😄

The source code