如何获取工厂方法的类属性的接口类型

时间:2016-05-10 17:13:36

标签: typescript

我的一些代码中有一个工厂方法,我不仅要传递一个Class类型来实例化,还要传递一组类中属性的默认值。我想得到这样的输入,编译器将告诉我是否尝试传入实例化的类类型中不存在的属性。我不知道怎么做。

这是一个简单的例子,展示了我想做的事情。

当我尝试它时,我得到一个编译器错误:“接口可能只扩展一个类或另一个接口。”对于接口ClassProps,因为它无法从T扩展。

class Base {
   myId: number;
}

class Klass1 extends Base {
   myString: string;
   myNumber: number;
}

interface IBaseCtr<T> {
   new (): T;
}

// Get an interface that is the properties of the class passed
interface ClassProps<T extends Base> extends T {}

// This method compiles, but doesn't check the prop names
//function factory<T extends Base>(ctrFunc: IBaseCtr<T>,
//                                 initialData?: Object): T

function factory<T extends Base>(ctrFunc: IBaseCtr<T>,
                                 initialData?: ClassProps<T>): T
{
   let new_obj = new ctrFunc();
   Object.assign(new_obj, initialData);
   return new_obj;
}


let v1 = factory(Klass1, {myId: 10, myString: 'foo'});

let v2 = factory(Klass1, {badVar: 10});

任何想法如何输入initialData,以便将badVar标记为在第二次通话中不被允许?

2 个答案:

答案 0 :(得分:1)

我认为这是一个可爱的问题。

这是我的方法。完全删除该界面。

function factory<T extends V, V extends Base>(ctrFunc: IBaseCtr<T>,
                                 initialData?: V): T
{
   let new_obj = new ctrFunc();
   Object.assign(new_obj, initialData);
   return new_obj;
}

这设置了一个层次结构,Base是V中属性的子集,它本身是T(最终类型)中属性的子集。 v1被分配没有错误。 v2的赋值语句在编译时导致错误。

您实际上可以继续删除extends Base部分以使此功能适用于更多地方。它仍然会以同样的方式进行类似的检查。

对于giggles,一个完全独立的工厂函数签名,类型检查正确:

function factory<T extends V, V>(ctrFunc: new () => T,
                                 initialData?: V): T

答案 1 :(得分:0)

我做的事情与你做的有点不同,但首先是代码:

interface BaseProperties {
    myId: number;
}

class Base<T extends BaseProperties> {
    protected myId: number;

    init(props: T): void {
        this.myId = props.myId;
    }
}

interface Klass1Properties extends BaseProperties {
    myString: string;
    myNumber: number
}

class Klass1 extends Base<Klass1Properties> {
    private myString: string;
    private myNumber: number;

    init(props: Klass1Properties): void {
        super.init(props);

        this.myString = props.myString;
        this.myNumber = props.myNumber;
    }
}

interface IBaseCtr<T> {
    new (): T;
}

function factory<P extends BaseProperties, T extends Base<P>>(ctrFunc: IBaseCtr<T>, initialData?: P): T {
    let new_obj = new ctrFunc();
    new_obj.init(initialData);
    return new_obj;
}


let v1 = factory(Klass1, {myId: 10, myString: 'foo'});

let v2 = factory(Klass1, {badVar: 10});

这符合您的请求,因为第二次调用factory会导致编译错误。

我将班级成员更改为私有/受保护,而不是使用Object.assign我使用init方法分配值。
为什么?因为这是面向对象的事情。这样,您可以在使用super.init分配值时使用类的层次结构(如果您决定不在特定情况下,则不要调用它)。
基本上你有更多的控制方式。

The code in playground