我正在尝试为我的项目生成一个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> {}
如何在无需手动定义继承的类的情况下将属性定义添加到继承的类?
答案 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