Skip to content Skip to sidebar Skip to footer

How To Pass A String Or Class To A Method To Create Instance

I am to use the following method, it works by passing a type to it such as obj.addComponent(MyClass). This works just fine. I tried to modify the type parameter by adding | string

Solution 1:

There's no way to get the class using a name in javascript, it doesn't have something similar to the java ClassLoader. You can get around that by creating your own mechanism, and there are probably many ways to do so, but here are 3 options.

(1) Maintain a registry for your component classes:

constREGISTRY: { [name: string]: ComponentType<Component> } = {};

classComponent {}

classMyComponent1extendsComponent {}
REGISTRY["MyComponent1"] = MyComponent1;

classMyComponent2extendsComponent {}
REGISTRY["MyComponent2"] = MyComponent2;

typeComponentType<T extendsComponent> = {
    new(): T;
}

function factory<T extendsComponent>(type: ComponentType<T> | string): T {
    returntypeoftype === "string" ?
        newREGISTRY[type]() asT:
        newtype();
}

(code in playground)

If you go with this approach then I suggest to make the REGISTRY an object that holds the collection, that way you can add the ctor only and get the name from that.

There's a variant for this and that's to use a decorator:

function register(constructor: typeof Component) {
    REGISTRY[(constructoras any).name] = constructor;
}

@registerclassMyComponent1extendsComponent{}

@registerclassMyComponent2extendsComponent{}

(code in playground)

(2) Wrap the components in a namespace (As @Shilly suggested in a comment):

namespace components {
    exportclassComponent {}
    exportclassMyComponent1extendsComponent {}
    exportclassMyComponent2extendsComponent {}

    exporttypeComponentType<T extendsComponent> = {
        new(): T;
    }

    exportfunctionforName(name: string): ComponentType<Component> {
        if (this[name] && this[name].prototypeinstanceofComponent) {
            returnthis[name];
        }
    }
}

function factory<T extends components.Component>(type: components.ComponentType<T> | string): T {
    returntypeoftype === "string" ?
        new (components.forName(type))() asT:
        newtype();
}

(code in playground)

If you're going with this approach then you need to make sure that all the component classes are exported.

(3) Use eval

classComponent {}
classMyComponent1extendsComponent {}
classMyComponent2extendsComponent {}

typeComponentType<T extendsComponent> = {
    new(): T;
}

function factory<T extendsComponent>(type: ComponentType<T> | string): T {
    returntypeoftype === "string" ?
        new (eval(type))() asT:
        newtype();
}

(code in playground)

This isn't a recommended approach, and you can read all about the cons in using eval in a lot of places. But it's still an option so I'm listing it.

Solution 2:

There is a way to instantiate classes by their name as String if they are in a namespace :

varvariableName: any = newYourNamespace[YourClassNameString](ClassParameters);

For exmaple, this should work :

namespace myNamespace {
    exportclassmyClass  {
        example() {
            returntrue;
        }
    }
}

varnewClass: any = new myNamespace["myClass"](); // <- This loads the class A.
newClass.example();

This will instantiate the class myClass using the string "myClass".

Thus, to come back to your situation, I think this will work :

namespace myNamespace {

    // The dependencies you definedexportclassComponent {

    }
    exportinterfaceComponentType<T extendsComponent> {
        new(): T;
    }

    // Just a class to contain the method's codeexportclassExample {
        public addComponent<T extendsComponent>(type: ComponentType<T> | string): T {
            letresult: T;
            if (typeoftype === "string") {
                result = new myNamespace[type]();
            } else {
                result = newtype();
            }
            return result;
        }
    }
}

Then, you'll be able to do this :

let stringToLoad = "Component";
let classToLoad = Component;

let example = new Example();

let result1: Component = example.addComponent(stringToLoad);
let result2: Component = example.addComponent(classToLoad);

Playground version with code + test : here

Post a Comment for "How To Pass A String Or Class To A Method To Create Instance"