打字稿联合类型一致性

Typescript union type consistency

有联合类型的 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 

有没有办法强制编译器拒绝这个?
check it out on typescriptlang.org/play

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

好的,所以我想我已经找到关于此的规范 GitHub 问题:microsoft/TypeScript#14150, a suggestion that "unsafe type-incompatible assignments should not be allowed". It's still an open issue (as of 2019-09-13) marked as "awaiting more feedback", so if you think you have a compelling use case that's not already mentioned in there you might want to comment in there. I wouldn't hold my breath waiting for this to be implemented, though, since the related issues like enforcing readonly strictness via flag flag and enabling variance annotations 要么已关闭,要么尚未采取行动。

这里的问题涉及到类型系统缺乏健全性。健全的类型系统只会让你做安全的事情。但在这里它允许您对可能违反对象声明类型的对象进行 属性 赋值。这种不安全的宽容意味着类型系统不健全。就其本身而言,这不被视为错误。 not one of TypeScript's design goals to "apply a sound or 'provably correct' type system". There is a tradeoff between correctness and productivity, and it is quite possible that fixing this issue might be more trouble than it's worth. See microsoft/TypeScript#9825 更多关于 TypeScript 的健全性 and/or 缺乏的讨论。

这里特别不合理:编译器假定将写入相同类型的属性是安全的,您可以读取 从它。通常情况并非如此,如您的示例所示,并且在此 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 引入了 change to indexed access writes (such as foo[bar] = baz) so that if the key is of a union type (say bar is Math.random()<0.5 ? "a" : "b") then you must write the intersection of the property types to it, not the union (so the type of baz must be typeof foo.a & typeof foo.b and will no longer accept typeof foo.a | typeof foo.b). This is a soundness improvement that prohibits some invalid things which were previously allowed. And it also prohibits lots of valid things which were previously allowed. And lots of people are still upset about it and new issues about it 仍然相当频繁地提交。我想如果他们解决了这个问题,这里也会发生同样的问题……你会得到你期望的错误,而且很多代码库都会崩溃。现在我想说你应该避免做这些作业,我知道这并不是什么安慰。

总之,希望这些信息对您有所帮助。祝你好运!