TypeScript扩展返回类型含keyof this的抽象类类型不兼容如何解决
报错底层逻辑
- TS类中的
this属于多态this类型,其实际指向的是当前调用方法的实例所属的具体类型,而非方法定义时所在的类的类型。 - 父类
AbstractCard中getKeys的签名为getKeys(): Array<keyof this>,当参数被声明为AbstractCard类型时,TS会将this绑定为AbstractCard类型,因此返回值类型被推导为仅包含"foo"的(keyof AbstractCard)[]。 - 子类
Card中getKeys的返回值类型是(keyof Card)[],keyof Card是"foo" | "bar",联合类型范围比keyof AbstractCard更大。 - TS的类型兼容性要求子类方法返回值必须是父类方法返回值的子类型,而数组是协变类型,因此
(keyof Card)[]要兼容(keyof AbstractCard)[]的前提是keyof Card是keyof AbstractCard的子类型。但实际keyof AbstractCard才是keyof Card的子类型,反向不成立,因此触发类型报错。
其他可行解决方案
除了你已经使用的泛型函数方案外,还有以下几种可选方案:
方案1:父类方法固定返回值类型
直接在父类声明时将getKeys的返回值绑定为父类自身的键集合,子类重写时对额外键做类型断言:
abstract class AbstractCard { foo = 1 getKeys(): Array<keyof AbstractCard> { return ['foo'] } } class Card extends AbstractCard { bar = 2 getKeys() { return [...super.getKeys(), 'bar' as keyof AbstractCard] } }
适用场景:不需要在类型层面感知子类新增的键,仅需要保证运行逻辑正常。缺点是会丢失子类新增键的类型信息。
方案2:CRTP(奇怪递归模板模式)
父类声明为接收子类类型作为参数的泛型类,提前绑定getKeys的返回值为子类的键集合:
abstract class AbstractCard<T extends AbstractCard<any>> { foo = 1 getKeys(): Array<keyof T> { return ['foo' as keyof T] } } class Card extends AbstractCard<Card> { bar = 2 getKeys(): Array<keyof Card> { return [...super.getKeys(), 'bar'] } }
适用场景:需要在父类逻辑中感知子类的属性类型,同时要保留完整的类型安全。
方案3:放宽返回值类型限制
将getKeys的返回值声明为string[],完全放弃键的类型检查:
abstract class AbstractCard { foo = 1 getKeys(): string[] { return ['foo'] } } class Card extends AbstractCard { bar = 2 getKeys() { return [...super.getKeys(), 'bar'] } }
适用场景:仅需要运行时获取键列表,不需要任何静态类型校验,不推荐在对类型安全要求高的项目中使用。
内容的提问来源于stack exchange,提问作者Rmaiy
相关产品推荐
相关产品推荐

