19

I want to have a function like:

createEntity<TEntity>(): TEntity {
    return new TEntity();
}

In C#, we could write:

void TEntity CreateEntity<TEntity>() where TEntity : new()

How can I do this in TypeScript?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
mehrandvd
  • 8,806
  • 12
  • 64
  • 111

6 Answers6

35

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');
bastianowicz
  • 358
  • 2
  • 9
toskv
  • 30,680
  • 7
  • 72
  • 74
  • I am thinking if do this way, every time you create the instance using the `factory` function, you have to import the concrete class. e.g. `SomeClass` – Lin Du Aug 07 '19 at 04:40
  • for variable arguments you could change `type` to `(type: { new (...args): T }`, the second parameter to `...args` and the creation call to `return new type(...args);` – bastianowicz Oct 30 '20 at 08:49
6

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);
Community
  • 1
  • 1
4

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

Sumomo
  • 603
  • 1
  • 5
  • 18
  • Couldn't get this to work, it was returning an instance with type (typeof MyClass)[]. Removing the [] from the return type of factoryFun results in it returning an instance with typeof myClass. But it should be just MyClass. – wlf Mar 04 '20 at 18:33
  • @wlf Sorry about that! It was a while ago I posted the answer, but I found a more recent version of what I was doing and that definitely works. I have replaced the entire answer... please let me know if that works for you. – Sumomo Mar 04 '20 at 20:34
1

We have a similar situation in which we want a generic class to take a factory function that takes parameters to instantiate the type. The provided solutions here does not cover that scenario.

The TypeScript approach seems to be to define Factories using an interface that expects a parameterless constructor:

export interface QueryTypeFactory<T> {
    new () : T;
}

TypeScript will not accept a factory function that returns an instance of type T to be used in place of an actual type - even though behind the scenes constructors are just functions that returns functions that returns T.

What we found out is that you can achieve this, but you need to an unsafe cast of the factory method like this:

function dynamicFactory<T>(f: () => T): FactoryType<T> {
    return f as any as FactoryType<T>;
}

Now you can provide a factory function which can encapsulate a closure to provide dynamic behavior when instantiating objects:

function factory(argument : any) : DynamicType {
    return new DynamicType(argument);
}

And now you use the dynamic factory like this:

let argument = { ... };
new ThingThatCreatesEntities(dynamicFactory(() => factory(argument)));

The trick is the cast of the factory method to any and then to the required factory type. It's not pretty, but it works and you can hide that in your implementation.

The nice thing about this approach is that the dynamic factory can be substituted for an actual type. So you could also do:

new ThingThatCreatesEntities(StaticType);

Where the StaticType is a class with a parameterless constructor.

Simon Ejsing
  • 1,455
  • 11
  • 16
-1

This appears to work just fine.

 export abstract class GridRowEditDialogBase<T extends DataRow> extends DialogBase{ 
      protected activeRow: T = {} as T;
 }
-1

if the T type is interface the instance of T is look like :

let a={} as T

if it is class with constructor I guess you need factor function :

factory1<T>(ctor?: NoParamConstructor<T>):T{return new ctor};
let a=this.factory<T>()