How To Pass A String Or Class To A Method To Create Instance
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();
}
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{}
(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();
}
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();
}
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"