You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 17:45:03