来自这里How to infer a class method unknown return type and use it in a property

try 推断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
  }
}

这总是返回Base类型:

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<typeof this.initialize>的索引方式ReturnType<this['initialize']>typeof之间有什么区别?

推荐答案

The 100 type is implicitly generic:

polymorphic this type是在microsoft/TypeScript#4910中实现的,"通过 for each 类和接口提供一个隐含的[generic]类型参数,该参数对于包含类型本身来说是constrained". 从某种意义上说,您的课程相当于这些:

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()  
  }
}

Indexing a generic with a specific key widens to the constraint

类型this['initialize']indexed access type,意思是"this类型的initialize属性的类型".该索引访问类型也是隐式通用的.在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"].

发生这种情况的原因是,当您有一个通用值并使用非通用键对其进行索引时,编译器将自动将通用值调整为widen到其约束. 因此在AbstractClass中,this.initialize的类型将只是() => Base.因此typeof this.initialize不是多态的,并且子类不会看到任何不同. 见this comment on microsoft/TypeScript#33181.

出现that的原因本质上是为了性能和易用性.如果每次提到this个都保持通用性,那么编译器将不得不到处合成大量通用类型,从而损害性能.此外,这将使use个通用值变得更加困难,因为它们永远不会"兑现"到特定类型.因此,如果更改这一点,编译器会变得更慢,并且会出现很多代码损坏.


把这些放在一起,你就会得到答案;这与ReturnType无关.this["initialize"]typeof this.initialize是两种不同的类型;前者是通用的,而后者不是.

Playground link to code

Typescript相关问答推荐

替代语法/逻辑以避免TS变量被分配之前使用." "

如何在排版中正确键入中间件链和控制器链

返回同时具有固定键和变量键的对象的函数的返回类型

Angular 信号:当输入信号改变值时,S触发取数的正确方式是什么?

对数组或REST参数中的多个函数的S参数进行类型判断

我可以为情态车使用棱角形的路由守卫吗?

在类型脚本中的泛型类上扩展的可选参数

如何判断对象是否有重叠的叶子并产生有用的错误消息

为什么不能使用$EVENT接收字符串数组?

如果数组使用Reaction-Hook-Form更改,则重新呈现表单

如何合并两个泛型对象并获得完整的类型安全结果?

是否有可能避免此泛型函数体中的类型断言?

如何将spread operator与typescripts实用程序类型`参数`一起使用

TypeScrip:如何缩小具有联合类型属性的对象

字幕子集一个元组的多个元素

仅当类型为联合类型时映射属性类型

React中的效果挂钩在依赖项更新后不会重新执行

如何避免TS2322;类型any不可分配给类型never;使用索引访问时

TS 排除带点符号的键

Typescript 编译错误:TS2339:类型string上不存在属性props