尝试推断
initialize
方法返回什么并将该类型应用于 prop
。
此代码有效:
class Base {
method_1() {}
}
class Child extends Base {
method_2() {}
}
abstract class AbstractClass {
prop: ReturnType<this['initialize']>
abstract initialize(): Base
}
class UserClass2 extends AbstractClass {
initialize() {
return new Child()
}
someMethod() {
this.prop.method_1() // Valid, no TS error
this.prop.method_2() // Valid, no TS error
}
}
这始终返回基本类型:
class Base {
method_1() {}
}
class Child extends Base {
method_2() {}
}
abstract class AbstractClass {
prop: ReturnType<typeof this.initialize>
abstract initialize(): Base
}
class UserClass2 extends AbstractClass {
initialize() {
return new Child()
}
someMethod() {
this.prop.method_1() // Valid, no TS error
this.prop.method_2() // Error!
}
}
在
ReturnType
中使用索引方式ReturnType<this['initialize']>
和点符号typeof
的ReturnType<typeof this.initialize>
有什么区别?
this
类型是隐式通用的:
多态this
类型在microsoft/TypeScript#4910中实现,“通过为每个类和接口提供隐含的[generic]类型参数,该参数约束到包含类型本身”。从某种意义上说,你的课程相当于这些:
abstract class AbstractClass<T extends AbstractClass<T>> {
prop!: ReturnType<T['initialize']>;
abstract initialize(): Base
}
class UserClass2<T extends UserClass2<T>> extends AbstractClass<T> {
initialize() {
return new Child()
}
someMethod(this: T) {
this.prop.method_1()
this.prop.method_2()
}
}
使用特定键对泛型进行索引会扩大约束
类型this['initialize']
是索引访问类型,意思是“
initialize
类型的
this
属性的类型”。该索引访问类型也是隐式通用的。在
AbstractClass
的子类中,它将引用该子类的
initialize
属性。您可能期望,如果您有一个类型为
this
的值(例如名为
this
的值)并且您使用键
'initialize'
对它进行索引,您将获得该索引访问类型,因此
typeof this.initialize
与
this['initialize']
相同。但事实并非如此:
abstract class AbstractClass {
abstract initialize(): Base
goodInit!: this['initialize'];
// (property) AbstractClass.goodInit: this["initialize"]
badInit!: typeof this.initialize
// (property) AbstractClass.badInit: () => Base
}
您可以看到 badInit
是
() => Base
类型,它完全独立于
this
,而
goodInit
正如预期的那样是
this["initialize"]
类型。发生这种情况的原因是,当您有一个通用值并使用非通用键对其进行索引时,编译器将自动将通用值扩大到其约束。所以在
AbstractClass
内部,this.initialize
的类型就是
() => Base
。因此 typeof this.initialize
不是多态的,子类不会看到任何不同。请参阅 microsoft/TypeScript#33181 上的评论。之所以出现that,本质上是为了性能和易用性。如果每次提到
this
都保持通用,那么编译器将不得不在各处合成大量通用类型,从而损害性能。此外,这将使使用泛型值变得更加困难,因为它们永远不会“兑现”到特定类型。因此,如果要改变这一点,编译器会变得更慢,并且会出现很多代码损坏。
将它们放在一起,你就会得到答案;和ReturnType
关系不大。就是this["initialize"]
和
typeof this.initialize
是两种不同的类型;前者是通用的,而后者则不是。Playground 代码链接