我正在寻找一种为基类提供属性初始化程序的方法,该基类将被所有继承类重用。这是为了在继承类中允许必需的属性,而无需在每个类中手动定义构造函数。
class A extends Base {
/*
I don't want this line to throw "Property 'a' has
no initializer and is not definitely assigned in the constructor."
*/
a: number
}
const a = new A({ a: 123 })
我认为这样会有意义:
class Base<T> {
constructor(props: T) {
(Object.keys(props) as Array<keyof T>).forEach(key => {
(this as T)[key] = props[key]
})
}
base: number
}
class A extends Base<A> {
a: string
}
但是TS不喜欢(this as T)
-(this as any as T)
解决了这个问题,这显然是胡说八道。
此外,更重要的是,TS仍然认为a
尚未初始化(似乎不了解Base
构造函数)。不仅如此-它甚至不认识到Base自身的base
属性是在构造函数中初始化的。
这可以在TS中实现吗?能够class B extends A<B>
而不用在每个类中重写泛型逻辑的情况更进一步吗?
假设:
!
-我希望它尽可能安全地输入答案 0 :(得分:0)
首先,我将使用Object.assign
更清洁且本机支持的(或将很快使用),如果Bowser缺少对此功能的支持,则添加polyfill。要在Typescript中使用Object.assign
,您需要定位es2015,或使用"lib": ["es2015","dom","scripthost"]
最简单的解决方案是在属性的声明中使用!
(即确定的赋值声明)。这就是为此创建的,请参见PR。
class Base<T> {
constructor(props: T) {
Object.assign(this, props)
}
base!: number
}
class A extends Base<A> {
a!: string
}
如果您真的不想使用(!
),则可以使用函数来稍微更改继承。初始化的属性将需要在该类的extends
子句中指定。这使我们可以绕过确定的分配检查,但这并不是特别漂亮:
class Base<T> {
constructor(props: T) {
Object.assign(this, props)
}
base!: number // notyhing we can do about this one
}
function withProps<TProps>(t: new <T>(props: T) => Base<T>): new () => Base<TProps> & TProps {
return t as any;
}
class A extends withProps<{
a: string
}>(Base) {
m() {
this.a; //ok
}
}
我们也可以使用这种方法来启用您的最后一种情况,但这涉及更大的类型手术:
class Base<T> {
constructor(props: T) {
Object.assign(this, props)
}
base!: number // notyhing we can do about this one
}
type FirstArgument<T extends new <U>(props: any) => any> = T extends new <U>(props: infer A) => any ? A: never
type GenericInstanceType<T extends new <U>(props: any) => any> = T extends new <U>(props: any) => infer R ? R: never
function withProps<TProps>(){
return function<TClass extends new <T>(props: any) => Base<T>>(t:TClass): new (props: FirstArgument<TClass> & TProps) => GenericInstanceType<TClass> & TProps {
return t as any;
}
}
class A extends withProps<{ a: number }>()(Base) {
m(){
this.a
this.base;
}
}
class B extends withProps<{ b: string }>()(A){
m(){
this.a
this.b
}
}