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

IT技术 javascript typescript
2021-01-16 10:42:24

我希望能够实例化一个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)
}
6个回答

你可以试试:

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);
他不能用全局窗口代替 NodeJs 吗?
2021-03-17 10:42:24
需要一些额外的东西才能使module部分正常工作(原始问题中未指定)。var newInstance = Object.create(window["moduleName1"]["moduleName"]["ClassName"].prototype); 替换类似的东西var person = new entities.Mammal.Person();
2021-03-29 10:42:24
您可以使用命名空间,而不是使用“window”。例如 export namespace Model { export class Greeter{...} } 。然后你可以使用 Object.create(Model['Greeter'].prototype);
2021-03-31 10:42:24
在 TypeScript 1.8.9 和 SystemJS 中,这不起作用,因为window不包含导出的类。
2021-04-01 10:42:24
这很好;虽然我建议不要明确调用window. 即,如果他使用 NodeJs 怎么办?窗口对象不存在?
2021-04-06 10:42:24

当您使用 TypeScript 时,我假设您希望输入加载的对象。所以这里是示例类(以及一个接口,因为例如您选择加载许多实现之一)。

interface IExample {
    test() : string;
}

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

    }

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

所以你会使用某种加载器来返回一个实现:

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

目前,我们有一个演员表:<IExample>- 但是当添加泛型时,我们可以取消这个并使用泛型代替。它看起来像这样(记住它不是语言的一部分!)

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');
+1 表示也适用于最新的 TypeScript 的解决方案。我不得不添加一个演员来让我的版本现在工作:)。
2021-03-13 10:42:24
这对我有用。我唯一要添加的是对命名空间的一些说明(如上图所示)。在这个例子中: const il = new InstanceLoader(Model); const 员工 = il.getInstance('员工'); 模型是导出的命名空间。例如 export namespace Model { export class Employee{...} } 然后当你想从命名空间模型中加载 Employee 时, import { Model } from '.your_model_path' 所以不是使用 'window' 的上下文,你可以添加你的类到一个命名空间,然后从该命名空间按名称加载一个类。
2021-03-13 10:42:24

更新

要使其在最新的 TypeScript 中工作,您现在需要将命名空间强制转换为any. 否则你会得到一个Error TS7017 Build:Element implicitly has an 'any' type because type '{}' has no index signature.

如果您有一个特定的命名空间/module,对于您要创建的所有类,您可以简单地执行以下操作:

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

更新:没有命名空间使用 new (<any>window)[classname]()

在 TypeScript 中,如果你在命名空间之外声明一个类,它会为“类函数”生成一个 var。这意味着它是针对当前范围存储的(很可能,window除非您在另一个范围下运行它,例如 nodejs)。这意味着你可以这样做new (<any>window)[classNameString]

这是一个工作示例(所有代码,没有命名空间):

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

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

要了解它的工作原理,生成的 JS 代码如下所示:

var TestClass = (function () {
    function TestClass() {
    }
    TestClass.prototype.DoIt = function () {
        alert("Hello");
    };
    return TestClass;
}());
var test = new window["TestClass"]();
test.DoIt();
如果您不使用命名空间怎么办?是否有默认命名空间可供使用?
2021-03-21 10:42:24
整洁谢谢。在我的项目中,我没有明确声明 typescript 命名空间,而是从 index.ts 文件中导出类型。因此,我能够使用此解决方案执行以下操作。import * as MyNamespace from '/someFileWithExports'然后new MyNamespace[classNameString]
2021-03-21 10:42:24
@Learner:很好的问题。将其添加到答案中。只需用于new window[classname]()按名称构造一个非命名空间的 TypeScript 类(假设您在默认命名空间中运行代码)。
2021-03-29 10:42:24
谢谢好东西!
2021-03-31 10:42:24
应该是答案;-)
2021-04-04 10:42:24

这适用于带有 ES6 module的 TypeScript 1.8:

import * as handlers from './handler';

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

类在handlermodule中导出它们可以从其他module重新导出。

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

至于在参数中传递module名称,我无法使其工作,因为 ES6 module无法动态加载。

我终于找到了适合我的正确答案。谢谢!
2021-03-14 10:42:24
这就是我需要的!谢谢!
2021-04-01 10:42:24
极好的!谢谢你。我花了几个小时寻找消失类型的解决方案。这适用于 3.0.3 ES7
2021-04-03 10:42:24

另一种方法是动态调用文件 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');