Generic and typeof T in the parameters

Simon picture Simon · Jul 11, 2016 · Viewed 9k times · Source

In TypeScript I can define the type of a variable as the type of a class. For example:

class MyClass { ... }

let myVar: typeof MyClass = MyClass;

Now I want to use this with a generic class, something like this:

class MyManager<T> {
    constructor(cls: typeof T) { ... }
    /* some other methods, which uses instances of T */
}

let test = new MyManager(MyClass); /* <MyClass> should be implied by the parameter */

So, I want to give my manager class another class (its constructor), because the manager needs to retreive static information associated with the class.

When compiling my code, it says that it cannot find name 'T', where my constructor is.

Any idea how to solve it?

Answer

Nitzan Tomer picture Nitzan Tomer · Jul 11, 2016

You can use this type of constructors: { new (): ClassType }.

class MyManager<T> {
    private cls: { new(): T };

    constructor(cls: { new(): T }) {
        this.cls = cls;
    }

    createInstance(): T {
        return new this.cls();
    }
}

class MyClass {}

let test = new MyManager(MyClass);
let a = test.createInstance();
console.log(a instanceof MyClass); // true

(code in playground)


Edit

The proper way to describe a class type in typescript is using the following:

{ new(): Class }

For example in the typescript lib.d.ts ArrayConstructor:

interface ArrayConstructor {
    new (arrayLength?: number): any[];
    new <T>(arrayLength: number): T[];
    new <T>(...items: T[]): T[];
    (arrayLength?: number): any[];
    <T>(arrayLength: number): T[];
    <T>(...items: T[]): T[];
    isArray(arg: any): arg is Array<any>;
    readonly prototype: Array<any>;
}

Here you have 3 different ctor signatures plus a bunch of static functions.
In your case you can also define it like:

interface ClassConstructor<T> {
    new(): T;
}

class MyManager<T> {
    private cls: ClassConstructor<T>;

    constructor(cls: ClassConstructor<T>) {
        this.cls = cls;
    }

    createInstance(): T {
        return new this.cls();
    }
}