The Query
Within TypeScript, utilizing the keyword extends
allows for asserting a generic parameter as a 'subtype' of another type. For example:
// class Base {}
// class Child extends Base {}
// edited:
class Base { a = 1 }
class Child extends Base { b = 2 } // Child is considered a subclass of Base
class Test {
// assert T as a subtype of Base
hello<T extends Base>() { ... }
}
new Test().hello<Child>();
However, how can we assert a generic parameter to be a 'supertype' of another type? For instance:
class Test2 {
// HERE, how do we ensure T is a superclass of Child
hello<T ??? Child>() { ... }
}
new Test2().hello<Base>();
Scenario
// class Actions<T = unknown> {
// edited:
class Actions<T = never> {
execs: (() => T)[]
append(exec: () => T) {
this.execs.push(exec)
return this;
}
// HERE, how do we guarantee NewT is a superclass of T?
setT<NewT ??? T>() {
return this as Actions<NewT>;
}
}
// in order for the following to function
new Actions()
.setT<Child>()
.append(() => new Child())
// up until this point, all executions should produce instances of Child
// after this point, all executions should generate instances of Base
.setT<Base>()
.append(() => new Base())
.execs.map(e => e()) // => Base[]
If we attempt to utilize extends
as follows:
setT<NewT extends T>() {
return this as Actions<NewT>;
}
An error occurs:
Conversion of type 'this' to type 'Actions<NewT>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Type 'Actions<T>' is not comparable to type 'Actions<NewT>'.
Type 'T' is not comparable to type 'NewT'.
'NewT' could be instantiated with an arbitrary type which could be unrelated to 'T'.ts(2352)
While using unknown
can transform the return type, it limits expressing the desired type constraint within the function signature for setT
.