从带有通用参数的子类方法中调用构造函数

时间:2018-10-19 22:22:56

标签: typescript generics constructor subclass type-inference

我无法获得以下代码进行类型检查:

type MyFunctionConstructor<T, F extends MyFunction<T>> = new (
  f: (n: number) => T
) => F;

class MyFunction<T> {
  constructor(f: (n: number) => T) {
    this.f = f;
  }

  f: (n: number) => T;

  composeT(g: (t: T) => T) {
    return new (this.constructor as MyFunctionConstructor<T, this>)(n =>
      g(this.f(n))
    );
  }

  composeU<U>(g: (t: T) => U) {
    return new (this.constructor as MyFunctionConstructor<U, this>)(n =>
      g(this.f(n)) // tsc error here, see below
    );
  }
}

class MyFancyFunction<T> extends MyFunction<T> {}

我收到以下错误:

Type 'this' does not satisfy the constraint 'MyFunction<U>'.
Type 'MyFunction<T>' is not assignable to type 'MyFunction<U>'.
Type 'T' is not assignable to type 'U'.

我不想按名称(即new MyFunction(...))调用构造函数,以便如果fMyFunction子类(例如{{1 }})FancyFunctionf.composeT(g)也是如此。 f.composeU(g)中用于构造函数调用的as强制转换不适用于具有通用参数的更通用的composeT方法。我该如何处理额外的泛型composeU

(进行U类型检查的方法来自this answer。这个问题本质上是我无法在评论中进行的跟进。)

1 个答案:

答案 0 :(得分:1)

正如我在评论中提到的那样,这实际上不能在TypeScript的类型系统中表达(无论如何从TS3.1开始)。 TypeScript代表所谓的higher-kinded types的能力非常有限。

首先,您想说MyFunction<T>的所有子类本身必须在T中是通用的。也就是说,您不想扩展 type MyFunction<T>,而是扩展type constructor T ⇒ MyFunction<T>,它将类型T转换为{ {1}}。但是您不能这样做,因为那里有no general way to refer to type constructors in TypeScript (Microsoft/TypeScript#1213)

接下来,假设您可以扩展MyFunction<T>而不是T ⇒ MyFunction<T>,则需要TypeScript的polymorphic this尊重这一点,因此MyFunction<T>也是类型构造函数,{ {1}}是一种具体类型。然后您can't do that either (Microsoft/TypeScript#5845)

由于Microsoft / TypeScript#1213仍然是一个未解决的问题,并且处于“需要帮助”状态,因此希望您最终能够做到这一点。但是我不会屏住呼吸。如果您查看该问题,将会看到一些人使用的变通办法,但我认为它们太麻烦了,我无法推荐。

您可以尝试以下方法:

this

但是,如果您想捕获多态this<X>的精神,则需要为每个子类显式缩小定义:

  composeU<U>(g: (t: T) => U): MyFunction<U> {
    return new (this.constructor as any)((n: number) =>
      g(this.f(n)); 
    );
  }

在上面,我们使用declaration merging来缩小this的{​​{1}}方法。

无论如何,希望对您有所帮助。祝你好运!