我有一个包含多个子类的类,这几乎是一个魔术类,并且执行一些逻辑来帮助我与json api集成。
在这种情况下,该类称为Resource
,该类的工作是使其能够像Laravel模型一样使用它。因此,我扩展了该类,显然可以从该类中使用这些方法,但是在此子类上,我就有了变异器和其他逻辑,以这个示例为例。
class Resource {
constructor(attributes: Attributes) {
const keys: string[] = Object.keys(attributes);
keys.forEach((attributeKey: string) => {
this.setAttribute(attributeKey, attributes[attributeKey]);
Object.defineProperty(this, attributeKey, {
get: () => this.getAttribute(attributeKey),
set: (value: any) => this.setAttribute(attributeKey, value);
});
});
}
...
}
class User extends Resource {
setFirstNameAttribute(value: string): void {
this.attributes.firstName = value.toUpperCase();
}
}
这会将类上的firstName属性转换为全部大写,这由Resource
类的构造函数中定义的getter和setter完成。
一切正常,例如...
const user = new User({ firstName: 'John', lastName: 'Doe' });
console.log(user.firstName);
实际上会输出JOHN
,因此父类内部的所有逻辑都可以正常工作。
我唯一遇到的问题是类型,因为属性是动态设置的,我似乎无法找到一种获取此类型的方法,因为我希望对类中的方法进行类型提示,所以{ {1}}不会出错,但是user.find()
也不会出错,但是因为user.firstName
不是实际属性,所以我无法找到提示它的方法。
我尝试设置类似firstName
的内容,但不确定我是否完全理解这一点。
答案 0 :(得分:0)
您可以添加通用类型参数来表示属性的类型。天真的,您认为您可以将此通用类型参数添加到类的extends或implement子句(即class ResourceBase<T> extends T
)中,但是由于多种原因,这是不可能的。
您可以做的是定义基类,而不能动态访问键(即user.firstName
不起作用)。然后,您可以将此基础转换为泛型构造函数,该构造函数返回基类和类型参数(new <T>(attr: T) => ResourceBase<T> & T
)的交集。在继承该构造函数的情况下,要给出具体的类型(即不是类型参数),一切都会正常进行。
注意:在getAttribute
中也使setAttribute
和K extends keyof T
通用,以完全捕获每个属性的键和键类型。
class ResourceBase<T> {
constructor(protected attributes: T) {
const keys = Object.keys(attributes) as Array<keyof T>;
keys.forEach((attributeKey) => {
this.setAttribute(attributeKey, attributes[attributeKey]);
Object.defineProperty(this, attributeKey, {
get: () => this.getAttribute(attributeKey),
set: (value: any) => this.setAttribute(attributeKey, value);
});
});
}
getAttribute<K extends keyof T>(key: K) : T[K] {
return this.attributes[key];
}
setAttribute<K extends keyof T>(key: K, value: T[K]) {
this.attributes[key] = value;
}
}
const Resource: new <T>(attr: T) => ResourceBase<T> & T = ResourceBase as any
class User extends Resource<{
firstName: string,
lastName: string
}> {
setFirstNameAttribute(value: string): void {
this.attributes.firstName = value.toUpperCase();
}
}
const user = new User({ firstName: 'John', lastName: 'Doe' });
console.log(user.firstName);