TypeScript泛型继承场景下emit方法调用报错的修复咨询
问题描述
首先是定义的类型与类:
type SignatureRecord = Record<string, (...args: any[]) => any>; class CustomEventEmitter<K extends SignatureRecord> { emit<N extends keyof K>(name: N, ...args: Parameters<K[N]>): void { } }
这段代码用于接收键为字符串、值为函数的对象。但编写以下代码时,Stateful类中的emit("update")调用会报错,而Component类中的emit("mount")调用运行正常:
class Emitter<M extends SignatureRecord> extends CustomEventEmitter<M> {} type StatefulSignatures = { update: () => any; }; class Stateful<M extends StatefulSignatures> extends Emitter<M> { protected triggerUpdate(): void { this.emit("update"); // 此处报错 } } type ComponentSignatures = StatefulSignatures & { mount: () => any; }; class Component extends Stateful<ComponentSignatures> { protected triggerMount(): void { this.emit("mount"); // 此处正常 } }
问题原因
Stateful类通过泛型M传递类型给Emitter,而M仅被约束为StatefulSignatures的子类型。TypeScript处理泛型时,无法确定泛型M必然保留update键的有效定义(尽管有继承约束,但理论上存在子类型覆盖该键的可能)。而Component类传递的是具体的ComponentSignatures类型,TypeScript可以明确识别mount是该类型的有效键,因此不会报错。
修复方案
方案一:显式指定emit的类型参数
在triggerUpdate方法中,明确指定emit的类型参数为"update",直接告知TypeScript该键属于M的键集合:
class Stateful<M extends StatefulSignatures> extends Emitter<M> { protected triggerUpdate(): void { this.emit<"update">("update"); } }
方案二:类型断言
将"update"断言为keyof M,消除TypeScript的类型歧义:
class Stateful<M extends StatefulSignatures> extends Emitter<M> { protected triggerUpdate(): void { this.emit("update" as keyof M); } }
方案三:收紧泛型约束
修改Stateful的泛型约束,确保M的update属性与StatefulSignatures中的定义完全一致:
class Stateful<M extends StatefulSignatures & { update: () => any }> extends Emitter<M> { protected triggerUpdate(): void { this.emit("update"); } }
方案四:给泛型添加默认值
如果Stateful的泛型M多数情况下就是StatefulSignatures,可以给泛型添加默认值,让TypeScript在未指定具体类型时使用默认类型推断:
class Stateful<M extends StatefulSignatures = StatefulSignatures> extends Emitter<M> { protected triggerUpdate(): void { this.emit("update"); } }
内容的提问来源于stack exchange,提问作者Jose Carlos Ramírez
相关产品推荐
相关产品推荐

