【问题标题】:Why is a discriminant needed for type guards of union types?为什么联合类型的类型保护需要判别式?
【发布时间】:2020-04-09 07:16:05
【问题描述】:

在下面的代码中,为什么typeof data.x === 'string'类型守卫不足以区分联合类型?

TS playground

interface A { x: string, y: number }
interface B { x: number, y: string }

function handler(data: A | B) {
    if (typeof data.x === 'string') {
        data.y // string | number --- WHUT?
    }
}

在什么情况下可以使用(无效)形状{ x: string: y: string } 调用handler

使用判别式,它可以工作(为什么?):

interface A { kind: 'A', x: string, y: number }
interface B { kind: 'B', x: number, y: string }

function handler(data: A | B) {
    if (data.kind === 'A') {
        data.y // number
    }
}

阅读Discriminated Unions 上的官方文档没有帮助。

他们只声明:

某些语言会自动为您区分工会;相反,TypeScript 建立在现有的 JavaScript 模式之上。

这并不能解释为什么 TS 在某些情况下可以使用类似的类型保护,而在联合的情况下却不能(我在第一个示例中没有看到任何歧义)。

【问题讨论】:

  • 判别式应该是 unique:在正确的示例中,您使用字符串文字 'A' 和 'B' 但在原始示例中(如果您正在考虑x 属性作为判别式)您只是使用字符串。换句话说,您的第一个示例实际上不是 TS 类型系统中的可区分联合。
  • 我知道这不是一个有区别的联合(根据定义,它需要一个判别式)。我的问题是,为什么它需要一个 - 即为什么我需要使它成为一个受歧视的工会?为什么我原来的类型保护还不够?
  • 只有区分联合才能让您通过检查其属性之一将联合类型的对象缩小到其组成部分之一的对象。您正试图通过检查x 属性将A | B 缩小到A,该属性仅在A | B 是可区分的联合并且x 是其判别式时才有效。但他们不是,所以你不能。
  • 是的,但这是循环推理。 true answer is --- 由于动机/时间/性能限制,而不是健全性,它没有实现。
  • 我认为这里的沟通问题是因为你在问“为什么我需要把它变成一个有歧视的工会?”而不是“为什么 TS 不认为这是一个受歧视的工会?”前一个问题会邀请诸如“您想要一个受歧视的工会,因为您试图根据其属性之一来区分工会;这就是受歧视的工会”之类的答案,而后一个问题会邀请诸如“缺乏执行这种特定模式的表现/动力/时间”。

标签: typescript discriminated-union union-types


【解决方案1】:

有区别的工会需要有一个非常特殊的结构。我曾经挖过规则here

只有在特定场景下,当属性被认为是联合的判别式时,才会缩小父对象的范围。如果满足以下条件,则将属性视为判别属性:

  • 该属性是一个文字类型,如此处所述#9163
  • 如果联合类型的联合类型包含至少一个单元类型且没有此处所述的可实例化类型#27695,则联合类型的属性将成为判别属性

如果不遵循这些规则,您最终只会得到字段区分而不是父对象区分。

【讨论】:

  • 谢谢,但为什么我首先需要判别式? (我不需要它成为真正意义上的歧视性工会)。好的,我的答案是阅读 GH 问题的其余部分:因为。您可以编辑您的答案以包含该内容,以便我接受吗?
  • @dwelle ? 是的.. 因为.. 它就是这样实现的..
猜你喜欢
  • 1970-01-01
  • 2020-01-14
  • 2017-05-18
  • 1970-01-01
  • 2019-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多