typescript - 打字稿中的传播运算符不保证正确的类型
问题描述
我正在使用扩展运算符创建具有更改字段的副本,如以下示例 ( run ) 所示。但是如果在创建对象字面量时更改了字段的类型,编译器不会抱怨:
class A{
constructor(readonly a: number, readonly b: string[]){}
copy(): A { // must return an object of type A
return {
...this,
b: 99 // should be of type string[]!
}
}
}
const a: A = new A(1, ["x", "y"])
const a1 = a.copy()
console.log(JSON.stringify(a1))
输出:
[LOG]: "{"a":1,"b":99}"
创建显式接口也无济于事:
interface IA {
readonly a: number
readonly b: string[]
}
...
copy(): IA { ...
看起来它与数组无关,因为下一个示例也会编译:
class A{
constructor(readonly a: number, readonly b: number){}
copy(): A {
return {
...this,
b: "my string" // should be of type number!
}
}
}
为什么这在 TypeScript 中是可能的?或者它是一个编译器错误?有没有办法避免
解决方案
这是以下类型系统事实之间的交互:
this
确实是多态this
类型。多态this
是扩展当前类的类的隐式泛型类型参数。(参考见最后一段)- 如果展开操作涉及泛型类型的操作数,则结果是展开的项目类型的交集。参考
- 一个交集可以分配给它的任何成分。因此,例如,该类型
A & B
可以分配给两者A
,并且无论和(示例)B
的任何属性之间是否存在任何不兼容性(参考参见分配兼容性)A
B
在这里应用这 3 个规则,我们得到{ ...this, b: 99 }
as this & { b: number }
( proof ) 的类型。可以分配给A
since this extends A
。
例如,如果您键入 assert this
to A
,您实际上会得到一个错误,因为不涉及通用操作数的扩展操作是正确键入的(ex)
至于这是否是一个错误,我将其称为设计限制。很长一段时间以来,使用带有泛型类型操作数的扩展是不可能的。此功能是在 3.2 ( PR ) 中添加的,阅读 PR 中的注释我们可以清楚地看到团队意识到了这里的一些漏洞:
使用交集的替代方法是按照#10727探索
{ ...T, ...U }
的路线引入高阶类型运算符。虽然这起初看起来很有吸引力,但实现这个新的类型构造函数并赋予它我们已经为交集类型实现的所有功能需要大量的工作,而且对于大多数场景来说,它几乎不会或根本没有提高精度。特别是,仅当扩展表达式涉及具有不同类型的重叠属性名称的对象时,差异才真正重要。此外,对于不受约束的类型参数,该类型实际上不能分配给,这虽然在技术上是正确的,但会令人讨厌。T
{ ...T, ...T }
T
添加了上面的重点,您的用例属于这个确切的问题。
推荐阅读
- javascript - axios CDN 链接被拒绝加载
- python - fig.canvas.tostring_rgb() 输出错误的长度字节,但仅在 Windows 上
- python - 将保持寄存器解码为二进制格式
- google-sheets - 如何使用谷歌财务API计算谷歌表格中每日收益的月度标准偏差
- mysql - 在 MySQL 5.7 中获取 Innodb 锁定线程 ID?
- customization - 我们可以为 Microsoft Teams 的应用程序创建自定义通知弹出窗口吗
- html - 想要在 HTML 的第五列的第一行打印值
- javascript - 如何在Javascript中过滤复杂的json文件
- jestjs - Svelte on:click 事件的单元测试
- svg - 转换 svg 父级导致模糊 - Firefox