Dynamically loading a typescript class (reflection for typescript)

This works in TypeScript 1.8 with ES6 module:

import * as handlers from './handler';

function createInstance(className: string, ...args: any[]) {
  return new (<any>handlers)[className](...args);
}

Classes are exported in handler module. They can be re-exported from other modules.

export myClass {};
export classA from './a';
export classB from './b';

As for passing module name in arugments, I can't make it work because ES6 module is unable to be dynamic loaded.


As you are using TypeScript I'm assuming you want the loaded object to be typed. So here is the example class (and an interface because you are choosing to load one of many implementations, for example).

interface IExample {
    test() : string;
}

class Example {
    constructor (private a: string, private b: string) {

    }

    test() {
        return this.a + ' ' + this.b;
    }
}

So you would use some kind of loader to give you back an implementation:

class InstanceLoader {
    constructor(private context: Object) {

    }

    getInstance(name: string, ...args: any[]) {
        var instance = Object.create(this.context[name].prototype);
        instance.constructor.apply(instance, args);
        return instance;
    }
}

And then load it like this:

var loader = new InstanceLoader(window);

var example = <IExample> loader.getInstance('Example', 'A', 'B');
alert(example.test());

At the moment, we have a cast: <IExample> - but when generics are added, we could do away with this and use generics instead. It will look like this (bearing in mind it isn't part of the language yet!)

class InstanceLoader<T> {
    constructor(private context: Object) {

    }

    getInstance(name: string, ...args: any[]) : T {
        var instance = Object.create(this.context[name].prototype);
        instance.constructor.apply(instance, args);
        return <T> instance;
    }
}

var loader = new InstanceLoader<IExample>(window);

var example = loader.getInstance('Example', 'A', 'B');

You could try:

var newInstance = Object.create(window[className].prototype);
newInstance.constructor.apply(newInstance, instanceparameters);
return newInstance;

Edit This version is working using the TypeScript playground, with the example:

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

//instance creation here
var greeter = Object.create(window["Greeter"].prototype);
greeter.constructor.apply(greeter, new Array("World"));

var button = document.createElement('button');
button.innerText = "Say Hello";
button.onclick = function() {
    alert(greeter.greet());
}

document.body.appendChild(button);

Update

To get this to work in latest TypeScript you now need to cast the namespace to any. Otherwise you get an Error TS7017 Build:Element implicitly has an 'any' type because type '{}' has no index signature.

If you have a specific namespace/module, for all the classes you want to create, you can simply do this:

var newClass: any = new (<any>MyNamespace)[classNameString](parametersIfAny);

Update: Without a namespace use new (<any>window)[classname]()

In TypeScript, if you declare a class outside of a namespace, it generates a var for the "class function". That means it is stored against the current scope (most likely window unless you are running it under another scope e.g. like nodejs). That means that you can just do new (<any>window)[classNameString]:

This is a working example (all code, no namespace):

class TestClass
{
    public DoIt()
    {
        alert("Hello");
    }
}

var test = new (<any>window)["TestClass"]();
test.DoIt();

To see why it works, the generated JS code looks like this:

var TestClass = (function () {
    function TestClass() {
    }
    TestClass.prototype.DoIt = function () {
        alert("Hello");
    };
    return TestClass;
}());
var test = new window["TestClass"]();
test.DoIt();