The code below is not passing the type-checking:
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> {}
The error message states:
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'.
The approach of calling the constructor by name (new MyFunction(...)
) is not desired, as it should allow instances of subclass of MyFunction
(for example, FancyFunction
) to work with f.composeT(g)
and f.composeU(g)
. The as
casting utilized for the constructor call in composeT
is not working for the more general composeU
method that involves a generic parameter. How can the additional generic, U
, be managed?
The solution for making composeT
type-check can be found in this answer. This question serves as a follow-up that couldn't be accommodated in a comment.