I want to create an instance of T
in a generic class in Typescript.
The following code is something I thought would work, but unfortunately it does not.
type Constructor<T> = { new(...args: any[]): T };
class Bar { }
class Foo<T extends Constructor<T>> {
constructor( /* some parameters here, but NOT a type creator! */ ) {}
create(): T {
return new T();
}
}
const bar: Bar = new Foo<Bar>().create();
Well there are some other questions/answers here on SO, but all use some kind of type creator that needs to be passed to the generic class/function, like so:
function create<T>(creator: new() => T): T {
return new creator();
}
const bar: Bar = create<Bar>(Bar);
But my ctor needs to not have something like that. The create
function should always stay parameter-less. Is there a way to make this work?
CodePudding user response:
In short: No it's not possible without a reference to a constructible runtime value.
The reason is that TypeScript types only exist at compile time (not at runtime) and can't be used as values. In your example, T
is only a type (not a runtime value). This is the JavaScript that your TypeScript program compilation would produce:
class Bar {}
class Foo {
constructor() { }
create() {
return new T();
}
}
const bar = new Foo().create();
// ^^^^^^^^
// Exception is thrown: ReferenceError: T is not defined
You can see that T
is a reference to a runtime value that doesn't exist, and so it throws a ReferenceError
exception when the create
method is invoked.
Instead, you can accept a constructible object (and its arguments) and return a constructed instance:
type Constructible<
Params extends readonly any[] = any[],
T = any,
> = new (...params: Params) => T;
class Foo {
static create <T extends Constructible>(
constructible: T,
...params: ConstructorParameters<T>
): InstanceType<T> {
return new constructible(...params);
}
}
class Bar {}
class Baz {
constructor (readonly param1: string, readonly param2: number) {}
}
const bar = Foo.create(Bar);
const baz = Foo.create(Baz, '2', 2);