【问题标题】:Typescript union type consistency打字稿联合类型一致性
【发布时间】:2019-09-13 12:53:33
【问题描述】:

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

【问题讨论】:

  • 虽然我确实认为这本身就是一个有趣的问题,但同时这确实感觉像 XY problem 所以如果你提供一点动机或背景可能会很有用你正在努力实现。也许您可以使用更合适的语法。
  • 您可以使用泛型类型别名:typescriptlang.org/docs/handbook/advanced-types.html
  • @apokryfos 这是一个普遍的问题,当开发人员仔细检查判别属性时,它可以简单地解决......关键是我希望 TS 编译器保持类型一致性 - 在编译时 -当类型定义明确时,毕竟这是 Typescript 的主要目的之一......显而易见的场景是:一个开发团队正在开发一个涉及 type A 的代码库,我想尽可能多地删除运行时错误可能我只是想知道是否有办法避免这个问题,或者它是否是 Typescript 功能缺乏
  • @Oleg 你能扩展你的建议吗?
  • 我的意思是,microsoft/TypeScript#9825 谈到了不正确的 TypeScript 行为的一般案例以及为什么它不能被消除,但我不能确定它是否与这个特定的实例。

标签: typescript union-types data-consistency


【解决方案1】:

好的,所以我想我已经找到了关于此的规范 GitHub 问题:microsoft/TypeScript#14150,建议“不应允许不安全的类型不兼容的分配”。它仍然是一个未解决的问题(截至 2019 年 9 月 13 日),标记为“等待更多反馈”,因此如果您认为您有一个尚未在其中提及的引人注目的用例,您可能需要在其中发表评论。不过,我不会屏住呼吸等待它的实施,因为像 enforcing readonly strictness via flag flagenabling variance annotations 这样的相关问题要么已关闭,要么尚未采取行动。

这里的问题涉及类型系统缺乏健全性。一个健全的类型系统只会让你做安全的事情。但在这里,它允许您对可能违反对象声明类型的对象进行属性分配。这种不安全的许可意味着类型系统是不健全的。这本身并不被视为错误。 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 为索引访问写入引入了change(例如foo[bar] = baz),因此如果键是联合类型(例如barMath.random()<0.5 ? "a" : "b"),那么您必须编写交集 的属性类型,而不是 union(所以baz 的类型必须是typeof foo.a & typeof foo.b 并且将不再接受typeof foo.a | typeof foo.b)。这是一个健全性改进,它禁止了一些以前允许的无效事情。它还禁止了许多以前允许的有效事物。很多人仍然对此感到不安,新的issues about it 仍然经常被提交。我想如果他们解决了这个问题,也会在这里发生同样的问题......你会得到你期望的错误,并且许多代码库会中断。现在我想说你可能应该避免做这些作业,我知道这并不是什么安慰。

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

【讨论】:

  • 感谢您提供有用的深入解释。这让我对 TS 有了更好的理解和洞察
【解决方案2】:

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

【讨论】:

  • 不幸的是,这不再是一个受歧视的工会……或者根本就不是工会。我假设这里的用例是所讨论的变量必须是联合类型,并且不能通过注释来缩小范围。如果我错了,@aleclofabbro 可以纠正我。
  • @jcalz 指出的 Oleg 是一个不同的用例,根本不涉及联合类型
猜你喜欢
  • 1970-01-01
  • 2017-05-18
  • 2021-08-05
  • 2018-12-06
  • 2020-11-30
  • 1970-01-01
  • 2023-01-10
  • 2021-01-26
  • 1970-01-01
相关资源
最近更新 更多