typescript - 打字稿联合类型一致性
问题描述
有一个联合类型的 Typescript 变量A
type A = {
b: true
x: number
} | {
b: false
x: string
}
declare const v: A
我可以正确地将属性分配x
给正确的类型,方法是使用 if 判别块检查属性b
值类型以保护type A
一致性
if (v.b) { // v.x is number
// ok for compiler
v.x = 3
// compiler error as v.x should be number
v.x = ''
} else { // v.x is string
// compiler error as v.x should be string
v.x = 3
// ok for compiler
v.x = ''
}
然而,外部判别块v.x
似乎正确number | string
,编译器不会抱怨分配给x
,number | string
尽管这会破坏type A
一致性
v.x = 3 // ok for compiler
v.x = '' // ok for compiler
有没有办法强制编译器拒绝这个?
在 typescriptlang.org/play 上查看
解决方案
好的,所以我想我已经找到了关于此的规范 GitHub 问题:microsoft/TypeScript#14150,建议“不应允许不安全的类型不兼容的分配”。它仍然是一个未解决的问题(截至 2019 年 9 月 13 日),标记为“等待更多反馈”,因此如果您认为您有一个尚未在其中提及的引人注目的用例,您可能需要在其中发表评论。不过,我不会屏住呼吸等待实现这一点,因为通过标志标志强制只读严格性和启用方差注释等相关问题要么已关闭,要么尚未采取行动。
这里的问题涉及类型系统缺乏健全性。一个健全的类型系统只会让你做安全的事情。但在这里,它允许您对可能违反对象声明类型的对象进行属性分配。这种不安全的许可意味着类型系统是不健全的。这本身并不被视为错误。TypeScript 的设计目标之一不是“应用声音或‘可证明正确’的类型系统”。在正确性和生产力之间进行权衡,解决这个问题很可能比它的价值更麻烦。有关 TypeScript 的健全性和/或缺乏的更多讨论,请参阅microsoft/TypeScript#9825 。
这里特别的不健全:编译器假定将相同类型写入您可以从中读取的属性是安全的。这通常不是真的,如您的示例所示,以及来自链接问题的相关示例:
interface A { kind: "A"; foo(): void; }
interface B { kind: "B"; bar(): void; }
function setKindToB(x: A | B): void {
x.kind = "B"; // clearly unsafe
}
那么可以做些什么呢?没有把握。TypeScript 3.5 引入了对索引访问写入(例如)的更改foo[bar] = baz
,因此如果键是联合类型(例如bar
is Math.random()<0.5 ? "a" : "b"
),那么您必须将属性类型的交集写入它,而不是联合(所以类型baz
必须是typeof foo.a & typeof foo.b
并且将不再接受typeof foo.a | typeof foo.b
)。这是一个健全性改进,它禁止了一些以前允许的无效事情。它还禁止了许多以前允许的有效事物。很多人仍然对此感到不安,还有关于它的新问题仍然相当频繁地提交。我想如果他们解决了这个问题,也会在这里发生同样的问题......你会得到你期望的错误,并且很多代码库会中断。现在我想说你可能应该避免做这些作业,我知道这并不是什么安慰。
无论如何,希望这些信息对你有用。祝你好运!
推荐阅读
- android - Nearby Connections 2.0:两个设备上的同时连接请求失败
- r - 具有 2 个变量和交互作用的加权回归
- java - Azure 针对 Azure B2C 活动目录验证 JWT 令牌
- javascript - 根据 ReactJS 中的日期显示可用座位
- javascript - 用于强制字段检查的弹出警报框和标签附近的星号符号 - JSP Form Spring mvc
- flutter - Flutter:为什么我不能在 AlertDialog 中使用图像?
- python-3.x - 将文件上传到 AWS 时无法捕获异常
- c# - 找到重叠的时间间隔并将它们分割成新的时间间隔
- java - 有没有办法在android studio中使用多个edittext和自定义数字键盘
- android - 如何使用 cordova-plugin-app-launcher 从已启动的应用程序中检索额外内容