如何用TypeScript优雅修复Pinia可混入通用Action的类型错误?
问题
尝试创建可混入多个Pinia store的通用Action,但遇到TypeScript类型不匹配问题。现有基础状态与Action代码如下:
import { Store, defineStore } from 'pinia' interface Actions<T> { a1: (this: _Store<T>) => void a2: (this: _Store<T>) => void } interface State {} interface Getters {} type _Store<T> = Store< string, State, Getters, Actions<T> > const makeActions = <T>(): Actions<T> => ({ a1() { // $patch is available here this.$patch({}) }, a2() { // a1 is available here this.a1() } })
尝试创建继承这些Action的store时:
const useMyStore = defineStore('MyStore', { state: (): State => ({}), actions: { ...makeActions<number>(), a3(this: _Store<number>) { this.$patch({}) this.a1() }, }, })
调用store方法时出现类型错误,提示扩展后的store的this与基础Action的this不匹配:
const store = useMyStore() // TS error : // 'this' context of type // 'Store<"MyStore", MyState, {}, { // a3(this: Store<number>): void; // a1: (this: _Store<number>) => void; // a2: (this: _Store<number>) => void; } // >' // is not assignable to method's 'this' of type '_Store<number>'. store.a1()
请问如何优雅地修复这些类型问题?
修复方案
核心问题是_Store<T>被硬编码为仅包含基础Actions,而实际生成的Store会包含自定义Actions(比如a3),导致this上下文类型不兼容。我们需要让通用Action的this类型适配扩展后的完整Store类型,而非固定的基础类型。
方案1:显式定义完整Store类型(类型安全最高)
- 调整通用Action的类型定义,让它依赖于完整的Store类型:
import { Store, defineStore } from 'pinia' // 基础Action类型,接收完整Store类型S作为泛型 type BaseActions<S extends Store> = { a1: (this: S) => void a2: (this: S) => void } // 通用Action创建函数,适配任意完整Store类型 const makeActions = <S extends Store>(): BaseActions<S> => ({ a1() { this.$patch({}) }, a2() { this.a1() } })
- 定义自定义Store的完整类型,包含自定义State、Actions:
// 自定义State interface MyState {} // 自定义Actions,继承基础Action interface MyActions extends BaseActions<MyStore> { a3: () => void } // 完整的Store类型 type MyStore = Store<'MyStore', MyState, {}, MyActions>
- 创建Store时指定完整类型,确保所有Action的
this上下文一致:
const useMyStore = defineStore<'MyStore', MyState, {}, MyActions>('MyStore', { state: (): MyState => ({}), actions: { ...makeActions<MyStore>(), a3() { this.$patch({}) this.a1() // 类型完全兼容 }, }, })
方案2:利用类型推断简化代码(最简洁)
如果不需要提前定义完整类型,可以让TypeScript自动推断最终Store的类型,无需手动指定泛型:
import { Store, defineStore } from 'pinia' // 直接让Action的this指向泛型参数S,由TypeScript自动推断 const makeActions = <S extends Store>() => ({ a1(this: S) { this.$patch({}) }, a2(this: S) { this.a1() } }) // 创建Store时,TypeScript会自动适配完整的this类型 const useMyStore = defineStore('MyStore', { state: () => ({}), actions: { ...makeActions(), a3() { this.$patch({}) this.a1() // 无类型错误 }, }, })
这种方式下,makeActions返回的Action会自动适配最终Store的this类型,无需额外类型声明,兼顾简洁性与类型安全。
内容的提问来源于stack exchange,提问作者sebpiq
相关产品推荐
相关产品推荐

