通过生成构造函数

时间:2016-10-30 15:06:32

标签: typescript

我正在制作一个用Typescript2.0编写的库,也可以从javascript中使用。

我有一个类Component和一个由Typescript制作的函数registerComponent

我需要通过调用Component来存储继承registerComponent的类的构造函数。注册的组件可以由我的库自动实现。 在某些情况下,方法的参数可能不是函数而是对象。我需要将对象转换为生成传递对象的构造函数。

但是,该构造函数也应该扩展Component类。 所以,我的意思是我想将一个类作为超类注入一个函数,该函数将使用typescript生成给定的对象。

这是处理registerComponent中的原始对象的部分。

const newCtor = function() {
  Component.call(this);
};
const properties = {};
for (let key in obj) {
  properties[key] = { value: obj[key] };
}
newCtor.prototype = Object.create(Component.prototype, properties);
return newCtor;

obj由用户给出普通对象。我认为这段代码可行,但实际上当我使用带有new关键字的构造函数时,此代码会填充错误Uncaught TypeError: Class constructor Component cannot be invoked without 'new'。 并且代码Component.call(this)抛出了该异常。

如何使用注入超类来创建有效的构造函数?

我很抱歉在这样暧昧的帖子中提问。但是现在我想我需要发布我想要完整的界面。

class Component{
    public baseFunction():string
    {
        return "This is base";
    }
}
class Registory{
   private static registeredConstructors:{[key:string]:(new()=>Component)};

   public static registerComponent(name:string,c:(new()=>Component)|{[key:string]:any}):void
   {
       if(typeof c === "function")
       {
           Registory.registeredConstructors[name] = c;
           return;
       }else{
           // Assume c is plain object
           // I need some code here to wrap c as constructor
       }
   }

   public static instanciate(name:string):Component
   {
       return new Registory.registeredContructors[name]();
   }
}

// When User want to register component via Typescript class

class C1 extends Component{
   public someProperty:string = "HELLO C1";

   public f1():string{
      return this.baseFunction() + this.someProperty;
   }
}

Registory.registerComponent("C1",C1);
const c1:Component = Registory.instanciate("C1");

// When user want to register component via plain object

Registory.registerComponent("C2",{
    someProperty:"Hello C2",
    f1:function(){
        return this.baseFunction() + this.someProperty;
    }
});
const c2:Component = Registory.instanciate("C2");

// This is the test c1 and c2 should pass

test.true(()=>c1 instanceof Component);
test.true(()=>c2 instanceof Component);
test.true(()=>c1.f1() === "This is base Hello C1");
test.true(()=>c2.f1() === "This is base Hello C2");
test.true(()=>c1 instanceof C1);

1 个答案:

答案 0 :(得分:1)

在我看来,如果我理解正确的话,您的方案可以更轻松地解决:

abstract class Component {
    constructor(props: any) {}
}

type ComponentConstructor = {
    new (props: any): Component;
    name: string;
};

const REGISTRY = {} as { [name: string]: ComponentConstructor };
function registerComponent(ctor: ComponentConstructor) {
    REGISTRY[ctor.name] = ctor;
}

function instantiateComponent(name: string, props: any): Component;
function instantiateComponent<T extends Component>(name: string, props: any): T {
    if (typeof REGISTRY[name] !== "function") {
        return null;
    }

    return new REGISTRY[name](props) as T;
}

class MyComponent1 extends Component { }
registerComponent(MyComponent1);

class MyComponent2 extends Component { }
registerComponent(MyComponent2);

let comp1 = instantiateComponent("MyComponent1", {}); // typeof comp1 is Component
let comp2: MyComponent2 = instantiateComponent("MyComponent2", {}); // typeof comp2 is MyComponent2

code in playground

修改

好的,既然我明白你想要什么,那就更容易提供帮助了 我仍然需要清理你的代码,但这就是你想要的:

interface IComponent {
    someProperty: string;
    f1(): string;
}

abstract class Component implements IComponent {
    abstract someProperty: string;
    abstract f1(): string;

    public baseFunction(): string {
        return "This is base ";
    }
}

type ComponentConstructor = { new (): Component };

abstract class ComponentFromObject extends Component {
    constructor(obj: IComponent) {
        super();

        Object.assign(this, obj);
    }
}

class Registory {
    private static registeredConstructors: { [key: string]: ComponentConstructor } = {};

    public static registerComponent(name: string, c: ComponentConstructor | IComponent): void {
        if (typeof c === "function") {
            Registory.registeredConstructors[name] = c;
        } else {
            Registory.registeredConstructors[name] = ComponentFromObject.bind(null, c);
        }
    }

    public static instanciate(name: string): Component {
        return new Registory.registeredConstructors[name]();
    }
}
const registory = new Registory();

// When User want to register component via Typescript class

class C1 extends Component {
    public someProperty: string = "Hello C1";

    public f1(): string {
        return this.baseFunction() + this.someProperty;
    }
}

Registory.registerComponent("C1", C1);
const c1: Component = Registory.instanciate("C1");

// When user want to register component via plain object

Registory.registerComponent("C2", {
    someProperty: "Hello C2",
    f1: function(){
        return this.baseFunction() + this.someProperty;
    }
});
const c2: Component = Registory.instanciate("C2");

// This is the test c1 and c2 should pass

console.log(c1 instanceof Component);
console.log(c2 instanceof Component);
console.log(c1.f1() === "This is base Hello C1");
console.log(c2.f1() === "This is base Hello C2");
console.log(c1 instanceof C1);

code in playground