Home > other >  Create instance of generic class parameter
Create instance of generic class parameter

Time:02-22

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:

TS Playground

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);

  • Related