The documentation on the official website includes a section explaining typescript support

Next.vuex.vuejs.org/guide/types…

First, try state, which looks like this:

  • A global state, getters
  • The steae and getters of both modules are open namespaces
const user = {
  namespaced: true.state: {
    flag: true
  },
  getters: {
    isTrue (state: userState): boolean {
      return state.loading
    }
  }
}

const foo = {
  namespaced: true.state: {
    count: 1
  },
  getters: {
    tenfold (state: fooState): number {
      return state.count * 10}}}const store = createStore({
  state: {
    age: 17
  },
  getters: {
    bar () => 1000000
  },
  modules:{
    foo,
    user
  }
})
Copy the code

The type is set according to the official document.

import { createStore, Store } from 'vuex'

// Configure the interface in the options
interface State {
  age: number
}

// This key needs to be used when a store is introduced in the component. There is no error --> useStore(key)
export const key: InjectionKey<Store<State>> = Symbol(a)const store = createStore<State>({
  ...
})

Copy the code

Below is the Store in the official D.ts file, as you can see, generics only pass their own defined State interface.

That’s about it, let’s introduce it in the component.

The state of the pit

Well, the code hints are only for the global state attribute, so how do we read the state attribute in modules?

The state of the module in store.state exists directly in the corresponding module name attribute.

Dude, attributes don’t exist. But we all know that typescript only does type inference, and that flapping in red doesn’t affect successful application execution. If you want to solve the flutters problem, you need to declare types according to typescript conventions. Also, type declarations have the added benefit of having code hints.

So let’s make a statement.

interface IUserState {
  flag: boolean
}

// It is a bit slow to write one by one, and it is tedious to have many attributes, so we can use ReturnType
// Separate the state from the file. If necessary, separate the state from the file
const userState = () = > {
  return{... }}export userState
export type UserState = ReturnType<userState>

// Same for other modules
Copy the code

Once declared, all you need to do is add it to the key, because that’s where the state is registered

// I have two Modules and add them together as a Modules
type Modules = {
  user: UserState;
  foo: FooState;
}
export const key: InjectionKey<Store<State & Modules>> = Symbol(a)Copy the code

Success prompt, also do not float red.

Next, try getters.

The problem with getters is the same: the code does not say, but it does not float red, but the method referenced is not store.getters. XXX, because the namespace is enabled, so the property name is changed.

setup() {
  const store = useStore(key)
  console.log(store.getters['user/isTrue']) // true
}
Copy the code

If you don’t need a code prompt, that’s it. But… Let’s start with the type declaration. The goal is to extract similar structures.

type Getters = {
  "user/isTrue": boolean;
  "foo/tenfold": number;
}
Copy the code
// This is our module object modules: {foo, user}
import { modules } from './modules'

// Retrieve the getters attribute from each module
type GetGetter<M> = M extends { getters: infer G } ? G : unknown
type GetGetters<Ms> = {
  [K in keyof Ms]: GetGetter<Ms[K]>
}
type getters = GetGetters<typeof modules>

// Extract to ['user/isTrue', 'foo/tenfold']
type addPrefix<K, N> = `${K & string}/${N & string}`
type MergeKey<Getters> = {
  [K in keyof Getters]: addPrefix<K, keyof Getters[K]>
}[keyof Getters]

/ / extraction
// type ModuleGetters = {
// "user/isTrue": (state: {
// flag: boolean;
// }) => boolean;
// "foo/tenfold": (state: {
// count: number;
// }) => number;
// }
type GetFn<T, A, B> = T[A & keyof T][B & keyof T[A & keyof T]] // It can only be passed down from one level to the next
type GetFinalKey<T> = {
  [K in MergeKey<T>]: K extends `${infer A}/${infer B}` ? GetFn<T, A, B> : unknown
}
type ModuleGetters = GetFinalKey<getters>

// Finally implement
// type Getters = {
// "user/isTrue": boolean;
// "foo/tenfold": number;
// }
type Getters = {
  [K in keyof ModuleGetters]: ReturnType<ModuleGetters[K]>
}

export { Getters }
Copy the code

Generics are written like functions. Now that you have a type declaration, find a place to add it. But as the Store diagram shows, getters is an any, and we can’t change its type by generics, meaning we can’t do the same with state.

We had to take the value out and wrap it ourselves. By the way, the official documentation suggests a wrap, because it would be troublesome to invoke the key once if different components used useStore(key). Well, let’s put them together.

// index.ts
import { createStore, Store, useStore as baseUseStore } from 'vuex'
import { Getters } from '@/store/utils'
export const key: InjectionKey<Store<State & Modules>> = Symbol(a)// Merge global and self-defined modules using type merging
type RootGetters = {
  bar: number
} & Getters

export function useStore () {
  // Take out what you need
  const { state, getters, commit, dispatch } = baseUseStore(key)
  return {
    state,
    getters: getters as RootGetters, // Maybe not elegant enough
    commit,
    dispatch
  }
}
Copy the code

Let’s see the effect! Remember to reference the useStore you exported

However, with the official mapState, the mapGetter is not so troublesome, and ts code does not need to be written.

However, there are still unsolvable problems

computed: { ... mapState({myAge: 'age'.foo: 'foo'.// The state of the module is only the outermost layer, but the official documentation shows no matching overload.
  }),
  ...mapGetters({
    isTrue: 'user/isTrue'})}Copy the code

In addition, there is also a method to configure this.$store. This method needs to be configured by useStore, but it is not very easy to configure.

The actions and mucations

Both dispatch and commit are returned in useStore and can be deconstructed. Use the same method as vex3, dispatch(‘ module name/method name ‘) and commit

conclusion

In general, TS support is relatively good, but it is a bit strange to ts newcomers, but the need to write TS is basically the type of input and output judgment.

I’m just a newbie. It’s my first time to write an article. Please point out my mistakes.