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



我在一些代码中有一个工厂方法,我不仅要在其中传递一个要实例化的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标记为不允许吗?

我认为这是一个很好的问题。

这是我的方法。完全删除该接口。

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部分,使该函数在更多地方适用。它仍然会以同样的方式进行打字检查。

对于咯咯笑,一个完全独立的工厂功能签名,类型检查正确:

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

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

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的第二次调用会导致编译错误。

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

操场中的代码

最新更新