typescript - 如何修复“类型的参数......不可分配给类型的参数......”错误?
问题描述
给定
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的键,对吧?
解决方案
正确的抽象类定义
首先,你在“作弊”。
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
}
}
initialize
您只是通过给它们相同的名称和兼容的函数签名(可选todos
参数)来欺骗 TS 认为您正在调用相同的方法。
你想要做的应该写成:
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 试图说服你回到正确的轨道。
推荐阅读
- image-processing - Three.js 中的 Inter_area 纹理 minFilter 是否像 opencv 中一样?
- java - java - 如何使用Java Swing手动控制哪个线程进入临界区?
- javascript - 对如何标记此内容感到困惑
- android - Android 清除 Back Stack 上的一些 Activity
- php - PHP月年日选择下拉列表在中间件laravel中生成日期格式
- python - 熊猫数据框添加数据框python
- android - 是否必须在 android 的主线程之外的房间中编写查询?
- python - 我想根据用户输入更新海龟
- android - 按下时升级到 dart 2.2.1 在图标按钮和凸起按钮中不起作用
- mysql - 解决 mysql 数据库中的 sequelize 布尔值