给予
abstract class A {
constructor() {
this.initialize()
}
initialize<T extends {
[t in keyof this]?: boolean
}>(todos?: T) {
// Do something
}
}
class B extends A {
initialize() {
super.initialize({ // <-- it will throw error starting on this open bracket
test: false
})
}
test() {
return 'test'
}
}
为什么上面的代码抛出错误说明
{ test: false }
不能分配给{[t in keyof this]?: boolean}
吗?虽然很明显。
'test'是B的键之一,对吧?并且keyof this
将指向B的键,对吧?
答案 0 :(得分:1)
首先,您在“作弊”。
abstract class A {
constructor() {
this.initialize()
// ~~~~~~~~~~ this `initialize` is from subclass
}
// different from this one here
initialize<T extends {
[t in keyof this]?: boolean
}>(todos?: T) {
// Do something
}
}
通过给TS提供相同的名称和兼容的函数签名(可选的initialize
参数),您就可以欺骗TS认为您正在调用相同的todos
方法。
您要执行的操作实际上应写为:
abstract class A {
constructor() {
this.initialize()
}
// fix 1: delcare abstract method
abstract initialize(): void
// fix 2: rename, mark as protected
protected _initialize<T extends {
[t in keyof this]?: boolean
// fix 3: todos probably isn't optional
}>(todos: T) {
// Do something
}
}
class B extends A {
initialize() {
super._initialize({
test: false
})
}
test() {
return 'test'
}
}
这是一种方式。子类知道基类,但是不是有义务知道所有扩展自身的子类。因为每个子类可以实现不同的东西,所以基类应该如何知道所有这些信息?
这就是抽象类的全部内容。您在基类上声明抽象方法或属性,这实际上是基类建立的契约。哪个子类想扩展我?先签合同!遵守我的条款!
回到您的情况,为什么_initialize
中的A
应该知道有关B
的任何信息?也可能有C
或D
扩展了A
。仅从keyof
推断 this
子类是不可能的。
因此,在调用时,您需要告诉super._initialize
哪些子类是this
。这是必要条件,而不是TS限制。
abstract class A {
constructor() {
this.initialize()
}
abstract initialize(): void
// pass subclass type as generic param B
protected _initialize<B>(todos: {
[t in keyof B]: boolean
}) {
// Do something
}
}
class B extends A {
initialize() {
super._initialize<B>({
test: true, // <-- this is correct now
foobar: true // <-- this triggers error report
})
}
test() {
return 'test'
}
}
您可以在JS中做很多修改工作,但不要将JS与TS混淆。 TS主要是关于最佳实践的,当您破坏它们时会遇到各种各样的怪癖和错误。您的情况不是TS的限制,而是TS试图说服您回到正确的轨道。
答案 1 :(得分:0)
@JGoodgive那我如何才能真正引用当前课程?因为我需要它指向当前班级,以便以后在班级的后代中使用。我可以通过以下方式做到这一点:
abstract class A<Model> {
constructor() {
this.initialize()
}
initialize<T extends {
[t in keyof Model]?: boolean
}>(todos?: T) {
// Do something
}
}
class B extends A<B> {
initialize() {
super.initialize({
test: true,
initialize: false,
})
}
test() {
return 'test'
}
}
但是将B传给B似乎很愚蠢,你不觉得吗?