Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically return a class constructor in TypeScript

I'm trying to build a mechanism in TypeScript to return a new instance of a class based on a string parameter representing the name of the class. Basically, I'm trying to create a factory, only I can't quite figure out how to do this in TypeScript. In pure JS, I could get away with this by just storing all the class constructors in a key/value object and calling new ClassDictionary[className](); - it's not the most robust solution, but it works in this situation. I can't seem to get the right combination of types and casting to accomplish this in TypeScript. Is there a better way? I haven't been able to find any examples of factory pattern in TypeScript, so right now I've just got a bit switch statement, which is ugly and inflexible.

like image 630
ken.dunnington Avatar asked Jun 26 '15 15:06

ken.dunnington


People also ask

What is a type type constructor in typescript?

The constructors of class can have types of their own. Above syntax, generic types are used, indicating that the constructor will return an object that can be a class instance of type T. Using the typed constructor, one can implement the factory design pattern in TypeScript.

What is the difference between typescript and ES5 Constructors?

The constructor is now explicitly defined - unlike the ES5 way which we had to assume / work out was in fact a constructor. One major difference is that in TypeScript we need to declare the class properties and their respective types upfront - typically you’d place these at the top of the class.

Which JavaScript functions are valid in typescript?

Therefore any valid JavaScript is also valid TypeScript. Let’s take a look at a simple Pizza constructor function, this is the ES5 way of creating a class and constructor, the function acting as our constructor: We pass the name argument through our constructor, and can simply type it as string.

How do you change a parameter to a class in typescript?

Parameter Properties. TypeScript offers special syntax for turning a constructor parameter into a class property with the same name and value. These are called parameter properties and are created by prefixing a constructor argument with one of the visibility modifiers public, private, protected, or readonly.


1 Answers

You can add a function declaration for every class in the dictionary, this can looks like this (Visit: https://www.typescriptlang.org/docs/handbook/declaration-merging.html):

class MyClass { 
    myMethod() { return "my return"; }
}

function factory(className: "Date"): Date;
function factory(className: "Number"): Number;
function factory(className: "MyClass"): MyClass;
function factory(className: string) { 

    var ClassDictionary = {
        "Date": Date,
        "Number": Number,
        "MyClass": MyClass
    };

    return new ClassDictionary[className]();
}

var d = factory("Date");

/* Its a date. */
alert(d.toISOString());

var n = factory("Number");

/* Now a number. */
alert(n.toPrecision());

var c = factory("MyClass");

/* And now MyClass. */
alert(c.myMethod());

Works in the Playground (Sample)

Or in a other way

// ...
var ClassDictionary = { };

ClassDictionary["Date"] = Date;
function factory(className: "Date"): Date;

ClassDictionary["Number"] = Number;
function factory(className: "Number"): Number;

ClassDictionary["MyClass"] = MyClass;
function factory(className: "MyClass"): MyClass;

function factory(className: string) { 
    return new ClassDictionary[className]();
}
// ...

Or with Arguments:

class MyClass {
    constructor(public myReturn: string) { }
    myMethod() { return this.myReturn; }
}

var ClassDictionary = { };

ClassDictionary["Date"] = Date;
function factory(className: "Date"): Date;

ClassDictionary["Number"] = Number;
function factory(className: "Number", value?: number): Number;

ClassDictionary["MyClass"] = MyClass;
function factory(className: "MyClass", myReturn: string): MyClass;

function factory(className: string, valueOrMyReturn?: number | string) { 
   if(valueOrMyReturn) return new ClassDictionary[className](valueOrMyReturn);
    return new ClassDictionary[className]();
}

var d = factory("Date");

/* Its a date. */
alert(d.toISOString());

var n = factory("Number", 42);

/* Now a number. */
alert(n.toPrecision());

var c = factory("MyClass", "This is the answer!");

/* And now MyClass. */
alert(c.myMethod());
like image 113
Markus Avatar answered Sep 19 '22 07:09

Markus