从继承的类定义动态生成的属性

时间:2018-07-25 08:41:35

标签: typescript typescript-definitions .d.ts

我正在尝试为我的项目生成一个d.ts声明文件。

有两个类可以计算繁琐的逻辑,其余的类将从它们继承。子类的属性不是在对象本身上定义的,而是在类内名为getter且在运行时定义的defaults上定义的。

基本模型

abstract class BaseModel {}
abstract class Model extends BaseModel {}

继承的模型

class Example extends Model {
    get defaults() {
       return {
            someProp: 1,
            anotherProp: 2
       }
    }
}

我的代码运行完美,但是动态添加的属性没有 autocomplete 。我尝试将以下内容添加到d.ts文件中,以使其了解动态属性,但它似乎不起作用。

index.d.ts

class Model<T> extends BaseModel {
    [P in keyof T]: T[P]
}

type ExampleType = {
    someProp : number
    anotherProp : number
}

class Example extends Model<ExampleType> {}

如何在无需手动定义继承的类的情况下将属性定义添加到继承的类?

1 个答案:

答案 0 :(得分:1)

您不能直接执行此操作,如果使用额外的函数创建该类,则可以执行该操作,该函数会使创建的类变型以添加属性:

type ReplaceInstanceType<T extends new (...args: any[])=> any, TNewInstance> = 
    T extends new (...args: infer U)=> any ? 
        new (...args: U) => TNewInstance : 
        never;

function createModel<T extends new (...args: any[])=> any>(modelClass: T) : 
    ReplaceInstanceType<T, InstanceType<T> & InstanceType<T>['defaults']> {
    return modelClass as any;
}

注意,它使用3.0功能Tuples in rest parameters and spread expressions 有关ReplaceInstanceType的2.9、2.8版本的信息,请参见this answer

我们可以通过以下两种方式之一来使用它:

直接使用createModel的输出:

const Example = createModel(class extends Model {
    constructor (data: string) {
        super();
        // this.anotherProp // Not ok we can't access teh extra fields inside the class
    }
    get defaults() {
        return {
            someProp: 1,
            anotherProp: 2
        }
    }
});
new Example(``).anotherProp // we can create the class and access the extra fields
new Example("").someProp

这具有以下缺点:额外的字段无法在类本身内部使用,这在某些情况下可能是一个问题。

第二种用法是在新类的extends子句中使用createModel,并且仅在传递给扩展的类中定义默认值,并在外部类中添加其他方法:

class Example extends createModel(class extends Model {
    get defaults() {
        return {
            someProp: 1,
            anotherProp: 2
        }
    }
}) {
    constructor(data: string) {
        super();
        this.anotherProp // new properties are accesible
    }
};
new Example(``).anotherProp // we can create the class and access the extra fields
new Example("").someProp

此方法的缺点是实际上创建了两个类,即我们实际使用的外部类和用于添加defaults属性的内部类。

修改

由于打字稿实际上在强类型化和验证键名方面做得非常好,因此您也可以考虑使用get/set方法,因此您可以获得良好的代码完成和类型验证,但这有点冗长:

abstract class BaseModel { 
    abstract get defaults();
    get<K extends keyof this['defaults']>(name: keyof this['defaults']): this['defaults'][K]{
        return this[name] as any;
    }
    set<K extends keyof this['defaults']>(name: keyof this['defaults'], value:  this['defaults'][K]) : void{
        this[name] =  value;
    }
}

class Example extends BaseModel {
    get defaults() {
        return {
            someProp: 1,
            anotherProp: 2
        }
    }

};
new Example().get('anotherProp')
new Example().get("someProp") 
new Example().set("someProp", 1) // 
new Example().set("someProp", "1") // error wrong type
new Example().get("someProp2")  // error