Javascript 动态加载typescript类(typescript的反射)

Javascript 动态加载typescript类(typescript的反射),javascript,typescript,Javascript,Typescript,我希望能够实例化一个typescript类,在运行时从中获取类和构造函数的详细信息。 我要编写的函数将接受类名和构造函数参数 export function createInstance(moduleName : string, className : string, instanceParameters : string[]) { //return new [moduleName].[className]([instancePameters]); (THIS IS THE BIT I

我希望能够实例化一个typescript类,在运行时从中获取类和构造函数的详细信息。 我要编写的函数将接受类名和构造函数参数

export function createInstance(moduleName : string, className : string, instanceParameters : string[]) {
    //return new [moduleName].[className]([instancePameters]); (THIS IS THE BIT I DON'T KNOW HOW TO DO)
}
你可以试试:

var newInstance = Object.create(window[className].prototype);
newInstance.constructor.apply(newInstance, instanceparameters);
return newInstance;
编辑此版本正在使用TypeScript操作,示例如下:

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);

当您使用TypeScript时,我假设您希望键入加载的对象。下面是示例类(以及一个接口,因为您选择加载许多实现中的一个)

因此,您将使用某种加载程序返回一个实现:

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;
    }
}
然后像这样加载它:

var loader = new InstanceLoader(window);

var example = <IExample> loader.getInstance('Example', 'A', 'B');
alert(example.test());
var-loader=newinstanceloader(窗口);
var example=loader.getInstance('example','A','B');
警报(例如.test());
目前,我们有一个cast:
——但是当添加泛型时,我们可以取消它,改用泛型。它看起来是这样的(记住它还不是语言的一部分!)

类InstanceLoader{
构造函数(私有上下文:对象){
}
getInstance(名称:string,…args:any[]):T{
var instance=Object.create(this.context[name].prototype);
instance.constructor.apply(实例,参数);
返回实例;
}
}
var loader=新的InstanceLoader(窗口);
var example=loader.getInstance('example','A','B');
,您可以执行以下操作:


这适用于带有ES6模块的TypeScript 1.8:

import * as handlers from './handler';

function createInstance(className: string, ...args: any[]) {
  return new (<any>handlers)[className](...args);
}
至于在Arumings中传递模块名,我无法让它工作,因为ES6模块无法动态加载。

Update 要在最新的TypeScript中实现这一点,您现在需要将名称空间强制转换为
any
。否则会出现
错误TS7017 Build:Element隐式地具有“any”类型,因为类型“{}”没有索引签名。

如果您有一个特定的命名空间/模块,那么对于要创建的所有类,您只需执行以下操作:

var newClass: any = new (<any>MyNamespace)[classNameString](parametersIfAny);
要了解其工作原理,生成的JS代码如下所示:

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

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

  private someText: string;

  constructor(someText: string) {
    this.someText = someText;
  }

  public writeSomeText() {
    console.log(this.someText);
  }
}
interface Service<T> {
  new (param: string): T;
}

export class ServiceFactory<T> {

  public createService(ctor: Service<T>, param: string) {
    return new ctor(param);
  }

}
const factory: ServiceFactory<MyService> = new ServiceFactory<MyService>();
const service: MyService = factory.createService(MyService, 'Hello World');
service.writeSomeText();

另一种方法是动态调用文件并
new

// -->Import: it dynamically
const plug = await import(absPath);
const constructorName = Object.keys(plug)[0];

// -->Set: it
const plugin = new plug[constructorName]('new', 'data', 'to', 'pass');


我找到了另一种方法,因为在我的情况下,我无法访问窗口

要创建的示例类:

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

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

  private someText: string;

  constructor(someText: string) {
    this.someText = someText;
  }

  public writeSomeText() {
    console.log(this.someText);
  }
}
interface Service<T> {
  new (param: string): T;
}

export class ServiceFactory<T> {

  public createService(ctor: Service<T>, param: string) {
    return new ctor(param);
  }

}
const factory: ServiceFactory<MyService> = new ServiceFactory<MyService>();
const service: MyService = factory.createService(MyService, 'Hello World');
service.writeSomeText();
工厂级:

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

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

  private someText: string;

  constructor(someText: string) {
    this.someText = someText;
  }

  public writeSomeText() {
    console.log(this.someText);
  }
}
interface Service<T> {
  new (param: string): T;
}

export class ServiceFactory<T> {

  public createService(ctor: Service<T>, param: string) {
    return new ctor(param);
  }

}
const factory: ServiceFactory<MyService> = new ServiceFactory<MyService>();
const service: MyService = factory.createService(MyService, 'Hello World');
service.writeSomeText();
接口服务{
新建(参数:字符串):T;
}
出口级服务工厂{
公共createService(ctor:Service,param:string){
返回新的ctor(参数);
}
}
然后使用工厂创建实例:

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

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

  private someText: string;

  constructor(someText: string) {
    this.someText = someText;
  }

  public writeSomeText() {
    console.log(this.someText);
  }
}
interface Service<T> {
  new (param: string): T;
}

export class ServiceFactory<T> {

  public createService(ctor: Service<T>, param: string) {
    return new ctor(param);
  }

}
const factory: ServiceFactory<MyService> = new ServiceFactory<MyService>();
const service: MyService = factory.createService(MyService, 'Hello World');
service.writeSomeText();
const-factory:ServiceFactory=new-ServiceFactory();
const-service:MyService=factory.createService(MyService,'helloworld');
service.writeMetext();

在某些特殊情况下,使用
eval
是合理的:

namespace org {
  export namespace peval {
    export class MyClass {
      constructor() {

      }

      getText(): string {
        return 'any text';
      }
    }
  }
}

const instance = eval('new org.peval.MyClass();');

console.log(instance.getText());
注意:如果不一定需要,则不应使用
eval
,因为它将以调用方的权限执行字符串中包含的代码。见:


在安全的情况下,当您知道代码字符串的来源和作用时(尤其是当您知道它不是来自用户输入时),您可以使用它。在上述案例中,我们使用有关TypeScript类名及其包的知识来创建一个新实例。

这很好;尽管我建议不要显式调用
窗口
。如果他用的是NodeJs怎么办?窗口对象不存在?他不能用global代替NodeJs吗?需要一些额外的东西才能使模块部分正常工作(这在最初的问题中没有指定)
var newInstance=Object.create(窗口[“moduleName1”][“moduleName”]
[“ClassName”].prototype);替换像
var person=new entities.dammal.person()这样的内容
在TypeScript 1.8.9和SystemJS中,这不起作用,因为
window
不包含导出的类。您可以使用名称空间来代替“window”。e、 导出名称空间模型{export class Greeter{…}。然后可以使用Object.create(Model['Greeter'].prototype);应该是答案;-)如果不使用名称空间怎么办?是否有默认名称空间可供使用?@Learner:问得好。在答案中添加了这一点。只需使用
newwindow[classname]()
按名称构造一个无名称空间的TypeScript类(假设您在默认名称空间中运行代码)。谢谢。在我的项目中,我没有显式声明typescript命名空间,而是从index.ts文件导出类型。因此,我能够使用此解决方案来执行以下操作
import*作为MyNamespace从'/someFileWithExports'
导入,然后
newmynamespace[classNameString]
谢谢大家!我只是想让你知道,我在互联网上搜寻这个问题的解决方案已经快4个小时了。你的答案是唯一适合我的。我不敢相信这是如此模糊,我会认为动态加载多态子类会更常见。很久以来,我甚至不记得回答这个问题或它做了什么。。。确定接受的答案对你没有帮助@user2130130?不管怎么说,我很高兴我帮了别人,呵呵:)没门!你的答案是唯一有价值的。所有其他操作都涉及取消对全局对象的引用。你的是唯一一个允许我获得类类型的类。由于类实际上只是JS中的构造函数,这在一般情况下非常容易,而在TS中则非常困难。用例:根据序列化对象中包含的某种类代码,接收序列化对象并使用不同的类将它们转换为全功能类实例。对于很多应用来说是绝对必要的。帖子应该是@top。我只有字符串,没有类名。示例:让dummy='className';createHandler(dummy)-对于同样适用于最新TypeScript的解决方案,将不会像dummy is string+1那样工作。