[英]Dynamically loading a typescript class (reflection for 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 playground,示例如下:
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 時,我假設您希望輸入加載的對象。 所以這里是示例類(和一個接口,因為您選擇加載許多實現之一,例如)。
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');
要使其在最新的 TypeScript 中工作,您現在需要將命名空間轉換為any
。 否則,您會收到Error TS7017 Build:Element implicitly has an 'any' type because type '{}' has no index signature.
如果你有一個特定的命名空間/模塊,對於你想要創建的所有類,你可以簡單地這樣做:
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();
這適用於帶有 ES6 模塊的 TypeScript 1.8:
import * as handlers from './handler';
function createInstance(className: string, ...args: any[]) {
return new (<any>handlers)[className](...args);
}
類在handler
模塊中導出。 它們可以從其他模塊重新導出。
export myClass {};
export classA from './a';
export classB from './b';
至於在參數中傳遞模塊名稱,我無法使其工作,因為 ES6 模塊無法動態加載。
作為打字稿0.9.1的,你可以做這樣的事情的游樂場:
class Handler {
msgs:string[];
constructor(msgs:string[]) {
this.msgs = msgs;
}
greet() {
this.msgs.forEach(x=>alert(x));
}
}
function createHandler(handler: typeof Handler, params: string[]) {
var obj = new handler(params);
return obj;
}
var h = createHandler(Handler, ['hi', 'bye']);
h.greet();
另一種方法是動態調用文件並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');
我找到了另一種方法,因為在我的情況下我無法訪問 window.open 。
要創建的示例類:
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();
function fromCamelCase(str: string) {
return str
// insert a '-' between lower & upper
.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
async getViewModelFromName(name: string) {
//
// removes the 'ViewModel' part ('MyModelNameViewModel' = 'MyModelName').
let index = name.indexOf('ViewModel');
let shortName = index > 0 ? name.substring(0, index) : name;
// gets the '-' separator representation of the camel cased model name ('MyModelName' = 'my-model-name').
let modelFilename = fromCamelCase(shortName) + '.view-model';
var ns = await import('./../view-models/' + modelFilename);
return new ns[name]();
}
要么
declare var require: any; // if using typescript.
getInstanceByName(name: string) {
let instance;
var f = function (r) {
r.keys().some(key => {
let o = r(key);
return Object.keys(o).some(prop => {
if (prop === name) {
instance = new o[prop];
return true;
}
})
});
}
f(require.context('./../view-models/', false, /\.view-model.ts$/));
return instance;
}
我正在使用typescript ~2.5.3
而且我能夠這樣做:
class AEmailNotification implements IJobStateEmailNotification {}
class ClientJobRequestNotification extends AEmailNotification {}
class ClientJobRequestAcceptedNotification extends AEmailNotification {}
class ClientJobRequestDeclinedNotification extends AEmailNotification {}
class ClientJobRequestCounterOfferNotification extends AEmailNotification {}
class ClientJobRequestEscrowedFundsNotification extends AEmailNotification {}
class ClientJobRequestCommenceNotification extends AEmailNotification {}
export function notificationEmail(action: string) {
console.log(`+ build factory object for action: ${action}`)
const actions = {}
actions['Create job'] = ClientJobRequestNotification
actions['Accept terms'] = ClientJobRequestAcceptedNotification
actions['Decline terms'] = ClientJobRequestDeclinedNotification
actions['Counter offer'] = ClientJobRequestCounterOfferNotification
actions['Add funds to escrow'] = ClientJobRequestEscrowedFundsNotification
actions['-- provider to commence the job --'] = ClientJobRequestCommenceNotification
const jobAction = actions[action]
if (!jobAction) {
console.log(`! unknown action type: ${action}`)
return undefined
}
return new jobAction()
}
在某些特殊情況下,使用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
因為它將以調用者的權限執行字符串中包含的代碼。 請參閱: eval() - JavaScript | MDN
在安全的情況下,當您知道代碼字符串來自何處以及它的作用時(尤其是當您知道它不是來自用戶輸入時),您可以使用它。 在上面描述的情況下,我們使用我們關於 TypeScript 類名及其包的知識來創建一個新實例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.