How to constraint a generic type to have new()?

The only way shown in the handbook to do something similar to this is to send the class you want to initialize as a parameter to the factory method, and describe it's constructor using the new keyword.

function factory<T>(type: { new (): T }): T {
    return new type();
}

class SomeClass { }

let result = factory(SomeClass);

The result will be of type SomeClass.

The constructor of the class will be type checked against the interface defined in the factory method.

If you want to initialize a class that takes a parameter in it's constructor you will have to specify that in the interface given to the factory method.

function factory<T>(type: { new (...args): T }, ...args): T {
    return new type(...args);
}


class SomeClass {
    constructor(name: string) { }
}

let a = factory(SomeClass, 'John Doe');

This has been answered here in Stack Overflow. To be able to new up a new class using generics, you need to have a guarantee that the type supports instantiation.

You can easily support parameter less constructors using this approach but with constructors that require parameters, it would become less useful as your creatEntity method should have to accept the values for those parameters and pass to the constructor while creating the new instance and as you can see each type can have its own signature for constructor.

class ObjectCreator{
    static createEntity<TEntity>(type:{new():TEntity;}):TEntity{
        return new type();
    }
}

class Person{
    firstName:string;
    lastName:string;
    constructor(){
        this.firstName = "TestFirstName";
        this.lastName = "TestLastName";
    }
}

var person: Person = ObjectCreator.createEntity(Person);
alert(person.firstName);

If you want to instantiate multiple classes with different parameters in your factory function then the other answers so far are incomplete. This is what you need.

class Hero {
  constructor(public point: [number, number]) {}
}

const entities = [];

const entityFactory = <
  T extends {
    new (...args: any[]): any;
  }
>(
  classToCreate: T,
  numberOf: number,
  ...args: ConstructorParameters<T>
): InstanceType<T>[] =>
  [...Array(numberOf)].map(() => new classToCreate(...args));

entities.push(...entityFactory(Hero, 10, [12, 10]));

console.log(entities[0].point);

Tags:

Typescript