ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Vuex TypeScript 支持实战:为 $store 属性与 useStore 函数提供完整类型

Vuex TypeScript 支持实战:为 $store 属性与 useStore 函数提供完整类型 Vuex TypeScript 支持实战为 $store 属性与 useStore 函数提供完整类型【免费下载链接】vuex️ Centralized State Management for Vue.js.项目地址: https://gitcode.com/gh_mirrors/vu/vuex本文围绕 Vuex 官方文档中的 TypeScript 支持指南展开系统讲解如何在 TypeScript 项目中为 Vuex 4 的三个关键点提供类型直接编写带类型的 Store 定义、为选项式组件中的this.$store属性声明模块扩展、以及为组合式 API 的useStore函数返回带类型的 Store。读完本文你将掌握从InjectionKey定义到app.use注入的完整类型链路并结合 Vuex 源码src/injectKey.js、src/store.js理解这些类型声明背后的运行时机制。Vuex 自带类型定义无需额外 TypeScript 配置Vuex 以库的形式内置了完整的类型声明typings因此你可以直接用 TypeScript 编写 Store 定义不需要任何针对 Vuex 的特殊 TypeScript 配置。项目本身的类型入口指向 types/index.d.tspackage.json 中通过typings: types/index.d.ts声明并将types/index.d.ts、types/helpers.d.ts、types/logger.d.ts、types/vue.d.ts一并发布files字段。对使用者而言只需按照 Vue 3 官方文档中「基本的 TypeScript 配置」来配置自己的项目即可。在类型层面Vuex 暴露的核心导出包括StoreS类与createStoreS(options: StoreOptionsS): StoreS工厂函数其中泛型S即 Store 的 state 类型useStoreS any(injectKey?: InjectionKeyStoreS | string): StoreS组合式函数StoreOptionsS、GetterTree、ActionTree、MutationTree、Module等用于描述完整 Store 结构的接口见 types/index.d.ts。也就是说只要你在createStore/new Store时给出 state 的接口定义store.state就会获得精确类型readonly state: Sdispatch、commit、subscribe、registerModule等 API 也都有对应的签名。为 Vue 组件中的$store属性提供类型虽然 Vuex 在运行时会自动把 Store 挂载到组件实例上——从 src/store.js 的install方法可以看到install (app, injectKey) { app.provide(injectKey || storeKey, this) app.config.globalProperties.$store this // ... }但 Vuex 并没有开箱即用地为this.$store提供类型。用 TypeScript 编写 Vue 组件时你必须自己声明模块扩展module augmentation。具体做法在项目的文件夹中新增一个声明文件例如vuex.d.ts对 Vue 的ComponentCustomProperties接口进行自定义类型声明// vuex.d.ts import { Store } from vuex declare module vue/runtime-core { // 声明你的 store state interface State { count: number } // 为 this.$store 提供类型 interface ComponentCustomProperties { $store: StoreState } }这里有两个值得注意的实现细节为什么扩展目标是vue/runtime-core而不是vue。Vue 3 的运行时核心类型包括ComponentCustomProperties、ComponentCustomOptions等可混入接口都定义在vue/runtime-core包中vue包只是再导出它们。Vuex 自身的类型文件 types/vue.d.ts 正是对同一个模块做扩展但它只声明了ComponentCustomOptions中的store?: Storeany用于createApp(Component, { store })这种传参写法并没有覆盖$store所以后者仍需用户自己声明。State接口的复用。上面声明在vue/runtime-core模块内的interface State只是该示例文档的写法实际项目中通常把State接口单独定义并从vuex.d.ts中导入避免声明合并带来的意外。声明生效后在选项式组件中this.$store.state.count即可被识别为numberthis.$store.commit/this.$store.dispatch也会走 types/index.d.ts 中Dispatch/Commit接口的重载签名同时支持type, payload与{ type, payload }两种调用风格。为useStore组合式函数提供类型用组合式 API 编写 Vue 组件时你通常希望useStore返回带类型的 Store。为了让useStore正确返回带类型的 Store需要完成三步定义带类型的InjectionKey向 Vue App 安装 Store 时传入这个带类型的InjectionKey把这个带类型的InjectionKey传给useStore方法。下面逐步说明。第一步定义带类型的 InjectionKey 并创建 Store// store.ts import { InjectionKey } from vue import { createStore, Store } from vuex // 定义 store state 的类型 export interface State { count: number } // 定义注入 key export const key: InjectionKeyStoreState Symbol() export const store createStoreState({ state: { count: 0 } })第二步安装 Store 时传入 InjectionKey// main.ts import { createApp } from vue import { store, key } from ./store const app createApp({ ... }) // 传入注入 key app.use(store, key) app.mount(#app)第三步在组件中通过 key 获取带类型的 Store// 在 Vue 组件中 import { useStore } from vuex import { key } from ./store export default { setup () { const store useStore(key) store.state.count // 类型是 number } }源码视角为什么 InjectionKey 是核心在内部实现上Vuex 使用 Vue 的 Provide/Inject 机制把 Store 安装到 Vue App 实例上这正是InjectionKey能承担类型传递职责的原因。从源码可以完整还原这条链路src/injectKey.js 定义了默认 key 与useStoreexport const storeKey store export function useStore (key null) { return inject(key ! null ? key : storeKey) }即传入 key 时用你传入的 key 去inject否则回退到字符串store。src/store.js 的install方法与之一一对应app.provide(injectKey || storeKey, this)—— 你通过app.use(store, key)传入的第二个参数就是injectKeyProvider 端和 Inject 端必须使用同一个 key类型才能沿 Provide/Inject 通道精确传递。类型声明与之一致types/index.d.ts 中Store.install的签名为install(app: App, injectKey?: InjectionKeyStoreany | string): void而useStore的签名是export function useStoreS any(injectKey?: InjectionKeyStoreS | string): StoreS;注意injectKey允许传入string这是因为运行时storeKey本身是字符串store。仓库自带的类型测试 types/test/index.ts 中的UseStoreFunction命名空间验证了全部四种调用形态传入InjectionKeystring形式的 key、传入字符串store、直接传泛型useStoreState()此时返回StoreState、以及不传任何参数的useStore()泛型默认any返回未类型化的 Store——这也解释了为什么文档强调想要类型就必须把带类型的InjectionKey从定义、安装到使用一路传下去。简化useStore的用法每次使用useStore时都要导入InjectionKey并把它传给useStore很快就会变成重复劳动。更简洁的做法是定义一个自己的组合函数来封装这个 key// store.ts import { InjectionKey } from vue import { createStore, useStore as baseUseStore, Store } from vuex export interface State { count: number } export const key: InjectionKeyStoreState Symbol() export const store createStoreState({ state: { count: 0 } }) // 定义你自己的 useStore 组合函数 export function useStore () { return baseUseStore(key) }这样组件侧只需导入自己的组合函数就不需要再提供注入 key 及其类型即可拿到带类型的 Store// 在 Vue 组件中 import { useStore } from ./store export default { setup () { const store useStore() store.state.count // 类型是 number } }由于baseUseStore(key)中key的类型是InjectionKeyStoreStatebaseUseStore的泛型S会被推断为State返回类型自然就是StoreState。这个模式相当于把「三步走」中第 1、2 步固化到 Store 模块内第 3 步的调用成本降为零。类型正确性的验证方式Vuex 仓库自身通过tsc对上述类型定义做回归验证package.json 的test:types脚本执行tsc -p types/test编译 types/test/index.ts 与 types/test/tsconfig.json开启了strict: true与noEmit: true。该测试文件覆盖了三条与本文直接相关的链路new Vuex.Store...各 APIdispatch/commit/watch/subscribe/subscribeAction/replaceState/registerModule/hotUpdate的调用形态见 types/test/index.ts 与 types/test/index.tsuseStore的 key 字符串、InjectionKey、泛型直传三种形态见 types/test/index.tsActionContextS, R在模块含 namespaced 模块中state/rootState/getters/rootGetters的推断见 types/test/index.ts。你可以在自己的项目中参照这套思路先用tsc --noEmit检查 Store 定义再在组件侧验证this.$store与useStore返回值的类型是否符合预期。小结场景需要的类型工作关键机制用 TS 编写 Store 定义无额外配置直接用createStoreState/new StoreState泛型S贯穿StoreOptionsS选项式组件访问this.$store在vuex.d.ts中扩展vue/runtime-core的ComponentCustomProperties运行时由app.config.globalProperties.$store挂载见 src/store.js组合式 API 使用useStore定义InjectionKeyStoreStateapp.use(store, key)useStore(key)Provide/Inject 通道见 src/injectKey.js简化组合式 API 用法在 Store 模块内封装一个闭包持有 key 的useStore()泛型推断自InjectionKey类型需要留意的前提本仓库对应 Vuex 4.x当前版本 4.1.0peerDependencies要求vue ^3.2.0因此上述app.use(store, key)、InjectionKey等写法均基于 Vue 3 应用若你的项目仍在使用 Vue 2 Vuex 3类型方案与注入机制均不相同本文内容不适用。【免费下载链接】vuex️ Centralized State Management for Vue.js.项目地址: https://gitcode.com/gh_mirrors/vu/vuex创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进