从继承的 class 定义动态生成的属性
Define dynamically generated properties from an inherited class
我正在尝试为我的项目生成一个 d.ts
声明文件。
有两个class计算重逻辑,其余class继承自它们。 subclasses 上的属性不是在对象本身上定义的,而是在 class 中名为 defaults
的 getter
上定义的,并在运行时定义。
基础模型
abstract class BaseModel {}
abstract class Model extends BaseModel {}
继承模型
class Example extends Model {
get defaults() {
return {
someProp: 1,
anotherProp: 2
}
}
}
我的代码运行完美,但没有自动完成 动态添加的属性。我尝试将以下内容添加到 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> {}
如何将 属性 定义添加到继承的 class 中而无需手动定义它们?
你不能直接这样做,如果你创建 class 使用一个额外的函数,你可以这样做,这将改变创建 class 以添加属性:
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 版本,请参阅
我们可以通过以下两种方式之一使用它:
直接使用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
这有一个缺点,即在 class 本身中无法使用额外的字段,这在某些情况下可能是个问题。
第二种用法是在新的class的extends子句中使用createModel
,只在传递给extends的class中定义默认值,在外部 class:
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
这种方法的缺点是实际上创建了 2 个 class 我们实际使用的外部方法和我们用来添加默认值的内部方法 属性。
编辑
由于 typescript 实际上在强类型和验证键名方面做得很好,您也可以考虑使用 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
我正在尝试为我的项目生成一个 d.ts
声明文件。
有两个class计算重逻辑,其余class继承自它们。 subclasses 上的属性不是在对象本身上定义的,而是在 class 中名为 defaults
的 getter
上定义的,并在运行时定义。
基础模型
abstract class BaseModel {}
abstract class Model extends BaseModel {}
继承模型
class Example extends Model {
get defaults() {
return {
someProp: 1,
anotherProp: 2
}
}
}
我的代码运行完美,但没有自动完成 动态添加的属性。我尝试将以下内容添加到 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> {}
如何将 属性 定义添加到继承的 class 中而无需手动定义它们?
你不能直接这样做,如果你创建 class 使用一个额外的函数,你可以这样做,这将改变创建 class 以添加属性:
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 版本,请参阅
我们可以通过以下两种方式之一使用它:
直接使用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
这有一个缺点,即在 class 本身中无法使用额外的字段,这在某些情况下可能是个问题。
第二种用法是在新的class的extends子句中使用createModel
,只在传递给extends的class中定义默认值,在外部 class:
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
这种方法的缺点是实际上创建了 2 个 class 我们实际使用的外部方法和我们用来添加默认值的内部方法 属性。
编辑
由于 typescript 实际上在强类型和验证键名方面做得很好,您也可以考虑使用 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