首页 > 解决方案 > 打字稿联合类型一致性

问题描述

有一个联合类型的 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
,编译器不会抱怨分配给xnumber | string尽管这会破坏type A一致性

v.x = 3   // ok for compiler 
v.x = ''  // ok for compiler 

有没有办法强制编译器拒绝这个?
在 typescriptlang.org/play 上查看

标签: typescriptunion-typesdata-consistency

解决方案


好的,所以我想我已经找到了关于此的规范 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,因此如果键是联合类型(例如baris Math.random()<0.5 ? "a" : "b"),那么您必须将属性类型的交集写入它,而不是联合(所以类型baz必须是typeof foo.a & typeof foo.b并且将不再接受typeof foo.a | typeof foo.b)。这是一个健全性改进,它禁止了一些以前允许的无效事情。它还禁止了许多以前允许的有效事物。很多人仍然对此感到不安,还有关于它的新问题仍然相当频繁地提交。我想如果他们解决了这个问题,也会在这里发生同样的问题......你会得到你期望的错误,并且很多代码库会中断。现在我想说你可能应该避免做这些作业,我知道这并不是什么安慰。

无论如何,希望这些信息对你有用。祝你好运!


推荐阅读