首页 > 解决方案 > 在 TypeScript 中返回抽象类中的子类

问题描述

我已将问题简化为以下情况:

abstract class Foo {
  child: Foo
  foo(): Foo {
    return this.child
  }
}
class Bar extends Foo {
  bar = true
}
const foo = new Bar()
// Property 'bar' does not exist on type 'Foo'. ts(2339)
foo.foo().bar

这个错误是不言自明且不足为奇的,但我想要实现的是foo()返回子类类型的东西。在这种情况下,Bar我可以使用它的属性bar。我们可以这样实现:

abstract class Foo<C extends Foo<C>> {
  child: C
  foo(): C {
    return this.child
  }
}
class Bar extends Foo<Bar> {
  bar = true
}
const foo = new Bar()
foo.foo().bar

耶,错误解决了!但是我也想在this里面Foo使用我使用的相同方式C,即子类类型:

abstract class Foo<C extends Foo<C>> {
  foo(): C {
    // Type 'this' is not assignable to type 'C'.
    // 'this' is assignable to the constraint of type 'C',
    // but 'C' could be instantiated with a different subtype of constraint 'Foo<C>'. ts(2322)
    return this
  }
}
class Bar extends Foo<Bar> {
  bar = true
}
const foo = new Bar()
foo.foo().bar

但我们不能,因为 TypeScript 不知道作为类型参数传递的子类类型实际上与实际的子类类型相同。

有没有办法解决这个不同的问题,这样我就不会出现这个错误?

我可以通过this像这样进行转换来解决它:this as unknown as C,让 TypeScript 知道相信我们它实际上是相同的,但是由于我在预期this的上下文中使用了大量代码C,这是我宁愿不做的事情。

编辑:我没有使用基类,而是开始使用一些包含共享功能的函数:

interface Foo {
  child: Foo
}
function fooFn<C extends Foo>(parent: C, child: C): C {
  return child
}
class Bar implements Foo {
  child: Bar
  bar = true
  foo() {
    return fooFn(this, this.child)
  }
}
const foo = new Bar()
foo.foo().bar

这对我的用例很有效,但是现在它变成了 20 个这样的函数,我觉得让这些方法直接调用函数的样板可以被基类替换,但后来我遇到了这个问题。

标签: typescripttypescript-generics

解决方案


您正在与打字稿的类型检查作斗争。

总是有它的hacky方式。any 类型用于返回 C:

return this as any

Typescript 正在做预期的事情,并保护您免受自己的伤害。将对象强制放入扩展类会导致数据丢失,因此您需要向编译器展示您知道这一点。


推荐阅读