具有联合类型的 Typescript 变量A
type A = {
b: true
x: number
} | {
b: false
x: string
}
declare const v: A
我可以通过使用 if 判别块检查属性b
值类型来保护type A
一致性,从而将属性x
正确分配给正确的类型
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 的设计目标之一不是"应用一个健全的或'可证明正确'的类型系统"。 正确性和生产力之间存在权衡,解决此问题很可能比它的价值更麻烦。请参阅 microsoft/TypeScript#9825 以了解有关 TypeScript 健全性和/或缺乏健全性的更多讨论。
这里特别不健全:编译器假定将相同的类型写入可以从中读取的属性是安全的。 这通常不是真的,如您的示例所示,以及链接问题的此相关示例所示:
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
是Math.random()<0.5 ? "a" : "b"
(,那么您必须将属性类型的交集写入它,而不是联合(因此baz
的类型必须typeof foo.a & typeof foo.b
并且不再接受typeof foo.a | typeof foo.b
(。 这是一项健全性的改进,禁止一些以前允许的无效内容。 它还禁止许多以前允许的有效事情。 很多人仍然对此感到不安,关于它的新问题仍然相当频繁地提交。 我想如果他们解决了这个问题,同样的问题也会在这里发生......你会得到你期望的错误,很多代码库会中断。 现在我想说你可能应该避免做这些任务,我知道这不是多少安慰。
无论如何,希望这些信息对您有用。 祝你好运!
泛型类型别名的代码示例 在你的情况下: 在 typescriptlang.org/play 上查看