Comment passer une chaîne ou une classe à une méthode pour créer une instance

Je dois utiliser la méthode suivante, cela fonctionne en passant un type tel que obj.addComponent(MyClass) . Cela fonctionne très bien.

J'ai essayé de modifier le paramètre de type en ajoutant | string | string à elle, mais il me donne maintenant des erreurs en disant:

Impossible d'utiliser «nouveau» avec une expression dont le type manque d'appel ou de signature de construction.

Est-ce que, de toute façon, est-ce que je peux modifier cela afin que je puisse transmettre un nom de classe ou une chaîne de la classe?

Voici ce que j'ai, cela ne fonctionne pas:

 public addComponent<T extends Component>(type: ComponentType<T> | string): T { let comp; comp = new type() as T; comp.name = comp.constructor.name; } 

Voici ses dépendances:

 class Component extends Obj { } interface ComponentType<T extends Component> { new(): T; } 

J'ai essayé d'utiliser Object.create() , qui fonctionne bien, mais je reçois une nouvelle erreur:

Uncaught TypeError: Impossible d'attribuer à la propriété de lecture seul 'nom' de l'objet '[object Object]'

Modifier:

À la fin, j'aimerais pouvoir passer ce qui suit à addComponent :

 obj.addComponent(MyClass); 

Ou

 obj.addComponent("MyClass"); 

Il n'y a aucun moyen d'obtenir la classe en utilisant un nom en javascript, il n'a pas d' ClassLoader java.
Vous pouvez contourner cela en créant votre propre mécanisme, et il existe probablement de nombreuses façons de le faire, mais voici 3 options.

(1) Maintenir un registre pour vos classes de composants:

 const REGISTRY: { [name: string]: ComponentType<Component> } = {}; class Component {} class MyComponent1 extends Component {} REGISTRY["MyComponent1"] = MyComponent1; class MyComponent2 extends Component {} REGISTRY["MyComponent2"] = MyComponent2; type ComponentType<T extends Component> = { new(): T; } function factory<T extends Component>(type: ComponentType<T> | string): T { return typeof type === "string" ? new REGISTRY[type]() as T: new type(); } 

( Code dans la cour de récréation )

Si vous utilisez cette approche, je suggère de faire de REGISTRY un objet qui détient la collection, de cette façon, vous pouvez ajouter le code uniquement et obtenir le nom de celui-ci.

Il y a une variante pour cela et c'est utiliser un décorateur:

 function register(constructor: typeof Component) { REGISTRY[(constructor as any).name] = constructor; } @register class MyComponent1 extends Component {} @register class MyComponent2 extends Component {} 

( Code dans la cour de récréation )

(2) Enroulez les composants dans un espace de noms (comme l'a suggéré @Shilly dans un commentaire):

 namespace components { export class Component {} export class MyComponent1 extends Component {} export class MyComponent2 extends Component {} export type ComponentType<T extends Component> = { new(): T; } export function forName(name: string): ComponentType<Component> { if (this[name] && this[name].prototype instanceof Component) { return this[name]; } } } function factory<T extends components.Component>(type: components.ComponentType<T> | string): T { return typeof type === "string" ? new (components.forName(type))() as T: new type(); } 

( Code dans la cour de récréation )

Si vous allez avec cette approche, vous devez vous assurer que toutes les classes de composants sont exportées.

(3) utiliser eval

 class Component {} class MyComponent1 extends Component {} class MyComponent2 extends Component {} type ComponentType<T extends Component> = { new(): T; } function factory<T extends Component>(type: ComponentType<T> | string): T { return typeof type === "string" ? new (eval(type))() as T: new type(); } 

( Code dans la cour de récréation )

Ce n'est pas une approche recommandée, et vous pouvez lire tout au sujet des inconvénients lors de l'utilisation d' eval dans de nombreux endroits.
Mais c'est toujours une option, alors je l'énumère.

Il existe un moyen d'instancier les classes par leur nom en chaîne si elles sont dans un espace de noms:

 var variableName: any = new YourNamespace[YourClassNameString](ClassParameters); 

Par exmaple, cela devrait fonctionner:

 namespace myNamespace { export class myClass { example() { return true; } } } var newClass: any = new myNamespace["myClass"](); // <- This loads the class A. newClass.example(); 

Cela créera une instanciation de la classe myClass utilisant la chaîne "myClass" .

Ainsi, pour revenir à votre situation, je pense que cela fonctionnera:

 namespace myNamespace { // The dependencies you defined export class Component { } export interface ComponentType<T extends Component> { new(): T; } // Just a class to contain the method's code export class Example { public addComponent<T extends Component>(type: ComponentType<T> | string): T { let result: T; if (typeof type === "string") { result = new myNamespace[type](); } else { result = new type(); } return result; } } } 

Ensuite, vous pourrez le faire:

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

Version de terrain de jeux avec code + test: ici