I have an abstract class Base
that accepts a type variable <T>
to be used within the class. Then I have many derived classes that explicitly define the type, such as class Derived extends Base<string> {...}
I want to have a variable (or array of variables) who's type can be any of those derived classes, no matter what <T>
is. Then I want to be able to use that variable to create new instances of these Derived classes.
Here's some code with what I have tried. From there, I am lost.
abstract class Base<T> {
abstract value: T;
}
class Derived extends Base<string> {
value = 'Hello world!';
}
class SecondDerived extends Base<number> {
value = 1234;
}
// This has type (typeof Derived | typeof SecondDerived)
let classes_A = [Derived, SecondDerived];
// This obviously works too, but with many derived classes can get long and tedious
let classes_B: (typeof Derived | typeof SecondDerived)[] = [];
classes_B.push(Derived);
classes_B.push(SecondDerived);
// This does NOT work
let classes_C: Base<any>[] = [];
classes_C.push(Derived); // "typeof Derived is not assignable to type Base<any>"
// This does NOT work
let classes_D: Base<unknown>[] = [];
classes_D.push(Derived); // "typeof Derived is not assignable to type Base<unknown>"
// This does NOT work
let classes_E: Base<string>[] = [];
classes_E.push(Derived); // "typeof Derived is not assignable to type Base<string>"
// This does NOT work
let classes_F: (typeof Base)[] = [];
classes_F.push(Derived); // "typeof Derived is not assignable to typeof Base"
My suggestion is this:
let classes: Array<new (...args: any) => Base<any>> = [];
classes.push(Derived); // okay
classes.push(SecondDerived); // okay
The element type of the array should be "a constructor of any subtype of Base<T>
for any T
". To say "a constructor of X
" as a type, you use a new
able signature, like new () => X
. Note that the signature specifies what types and number arguments the constructor expects; if you don't care, then you can use a rest argument of type any
or any[]
, like new (...args: any) => X
.
Since the type you're constructing is any subtype of Base<T>
for any T
and you presumably don't need to keep track of which, then Base<any>
is probably good enough. (If not, then please elaborate on a use case for which this doesn't work).
Okay, hope that helps; good luck!