给定
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 的 key ,对吗?
最佳答案
正确的抽象类定义
首先,你在“作弊”。
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
}
}
你只是通过给它们相同的名称和兼容的函数签名(可选的 todos
参数)来欺骗 TS 认为你正在调用相同的 initialize
方法。
你想做的应该写成:
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'
}
}
子类与基类的联系
这是一种方式。子类知道基类,但基类没有义务知道扩展自身的所有子类。因为每个子类可以实现不同的东西,基类应该如何知道所有这些信息?
这就是抽象类的全部意义所在。您在基类上声明抽象方法或属性,这实际上是基类建立的契约。哪个子类想要扩展我?先签契约(Contract)吧!遵守我的条款!
回到你的例子,为什么 A
中的 _initialize
应该知道关于 B
的任何事情? C
或 D
也可以扩展 A
。仅仅从 this
中推断 keyof
子类是不可能的。
所以调用时需要告诉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 中做很多 hacky 的事情,但不要将 JS 与 TS 混淆。 TS 主要是关于最佳实践,当你打破它们时,你会遇到各种怪癖和错误。你的情况不是TS的限制,那是TS试图说服你回到正轨。
关于typescript - 如何修复 "Argument of type ... is not assignable to parameter of type ..."错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55883031/