打字稿联合类型一致性

时间:2019-09-13 12:53:33

标签: typescript union-types data-consistency

具有类型为A的Typescript变量

type A = {
    b: true
    x: number
  } | {
    b: false
    x: string
  }

declare const v: A

通过使用if判别块检查属性x的值类型以保护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 

是否有一种方法可以强制编译器拒绝此操作?
check it out on typescriptlang.org/play

2 个答案:

答案 0 :(得分:1)

好的,所以我认为我已经找到有关以下内容的规范的GitHub问题:microsoft/TypeScript#14150,这是一个建议,“不应允许不安全的类型不兼容的分配”。截至2019-09-13,它仍然是一个待解决的问题,标记为“等待更多反馈”,因此,如果您认为那里有一个尚未被提及的引人注目的用例,则可能需要在此发表评论。不过,我不会屏住呼吸等待实现,因为enforcing readonly strictness via flag flagenabling variance annotations之类的相关问题已经关闭或尚未解决。

这里的问题涉及类型系统缺乏声音。声音类型系统只会让您做安全的事。但是在这里,它使您可以对可能违反该对象的声明类型的对象进行属性分配。这种不安全的放任态度意味着类型系统是 unsound 。就其本身而言,这不被视为错误。 not one of TypeScript's design goals是“应用声音或'证明正确的'类型系统”。在正确性和生产率之间要进行权衡,很可能解决此问题可能比其价值更大。有关TypeScript的健全性和/或缺乏性的更多讨论,请参见microsoft/TypeScript#9825

这里特别糟糕:编译器假定将相同类型的写入到可以从中读取的属性是安全的。如您的示例以及此related example from the linked issue所示,通常这是不正确的:

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)中引入了change,因此,如果键是联合类型(例如,barMath.random()<0.5 ? "a" : "b"),则必须编写属性的 intersection 而不是 union (因此baz的类型必须为typeof foo.a & typeof foo.b,并且将不再接受{{ 1}})。这是一个健全的改进,它禁止了以前允许的一些无效的事情。而且它还禁止了许多以前允许的有效物品。而且很多人对此仍然感到不满,并且新的issues about it仍被频繁提起。我想如果他们解决了这个问题,同样的问题也会在这里发生……您将得到您所期望的错误,并且许多代码库将被破坏。就目前而言,您可能应该避免执行这些作业,据我所知,这并不是什么安慰。

无论如何,希望信息对您有所帮助。祝你好运!

答案 1 :(得分:0)

您所用的通用类型别名的代码示例: check it out on typescriptlang.org/play