从具有泛型参数的子类方法调用构造函数
Calling constructor from a subclass method with a generic parameter
我无法获取以下代码以进行类型检查:
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(...)
),因此如果 f
是 MyFunction
的子类的实例(例如 FancyFunction
) 那么 f.composeT(g)
和 f.composeU(g)
也是如此。 composeT
中用于构造函数调用的 as
转换不适用于具有通用参数的更通用的 composeU
方法。我该如何处理额外的泛型,U
?
(进行composeT
类型检查的方法来自this answer。这个问题本质上是一个后续问题,我无法发表评论。)
正如我在评论中提到的,这在 TypeScript 的类型系统中无法真正表达(无论如何从 TS3.1 开始)。 TypeScript 表示所谓的 higher-kinded types 的能力非常有限。
首先,您想说 MyFunction<T>
的所有子类本身在 T
中必须是通用的。也就是说,您不想扩展 type MyFunction<T>
,而是扩展 type constructor T ⇒ MyFunction<T>
, which converts a type T
into a MyFunction<T>
. But you can't do that, because there's no general way to refer to type constructors in TypeScript (Microsoft/TypeScript#1213).
接下来,假设您可以扩展 T ⇒ MyFunction<T>
而不是 MyFunction<T>
,您将需要 TypeScript 的 polymorphic this
to respect that, so that this
is also a type constructor and this<X>
is a concrete type. And you can't do that either (Microsoft/TypeScript#5845).
由于 Microsoft/TypeScript#1213 仍然是一个悬而未决的问题并且处于 "help wanted" 状态,因此您最终有希望能够做到这一点。但我不会屏住呼吸。如果您查看该问题,您会看到一些人使用的解决方法,但在我看来,它们太麻烦了,我无法推荐。
您可以尝试类似的方法:
composeU<U>(g: (t: T) => U): MyFunction<U> {
return new (this.constructor as any)((n: number) =>
g(this.f(n));
);
}
但是如果你想捕捉多态的精神,你需要明确地缩小每个子类的定义 this
:
class MyFancyFunction<T> extends MyFunction<T> { }
interface MyFancyFunction<T> {
composeU<U>(g: (t: T) => U): MyFancyFunction<U>;
}
上面我们用declaration merging缩小了MyFancyFunction
的composeU
方法。
总之,希望对您有所帮助。祝你好运!
我无法获取以下代码以进行类型检查:
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(...)
),因此如果 f
是 MyFunction
的子类的实例(例如 FancyFunction
) 那么 f.composeT(g)
和 f.composeU(g)
也是如此。 composeT
中用于构造函数调用的 as
转换不适用于具有通用参数的更通用的 composeU
方法。我该如何处理额外的泛型,U
?
(进行composeT
类型检查的方法来自this answer。这个问题本质上是一个后续问题,我无法发表评论。)
正如我在评论中提到的,这在 TypeScript 的类型系统中无法真正表达(无论如何从 TS3.1 开始)。 TypeScript 表示所谓的 higher-kinded types 的能力非常有限。
首先,您想说 MyFunction<T>
的所有子类本身在 T
中必须是通用的。也就是说,您不想扩展 type MyFunction<T>
,而是扩展 type constructor T ⇒ MyFunction<T>
, which converts a type T
into a MyFunction<T>
. But you can't do that, because there's no general way to refer to type constructors in TypeScript (Microsoft/TypeScript#1213).
接下来,假设您可以扩展 T ⇒ MyFunction<T>
而不是 MyFunction<T>
,您将需要 TypeScript 的 polymorphic this
to respect that, so that this
is also a type constructor and this<X>
is a concrete type. And you can't do that either (Microsoft/TypeScript#5845).
由于 Microsoft/TypeScript#1213 仍然是一个悬而未决的问题并且处于 "help wanted" 状态,因此您最终有希望能够做到这一点。但我不会屏住呼吸。如果您查看该问题,您会看到一些人使用的解决方法,但在我看来,它们太麻烦了,我无法推荐。
您可以尝试类似的方法:
composeU<U>(g: (t: T) => U): MyFunction<U> {
return new (this.constructor as any)((n: number) =>
g(this.f(n));
);
}
但是如果你想捕捉多态的精神,你需要明确地缩小每个子类的定义 this
:
class MyFancyFunction<T> extends MyFunction<T> { }
interface MyFancyFunction<T> {
composeU<U>(g: (t: T) => U): MyFancyFunction<U>;
}
上面我们用declaration merging缩小了MyFancyFunction
的composeU
方法。
总之,希望对您有所帮助。祝你好运!