为什么联合类型的类型保护需要判别式?

Why is a discriminant needed for type guards of union types?

在下面的代码中,为什么 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 上的官方文档没有帮助。

他们只声明:

Some languages automatically discriminate unions for you; TypeScript instead builds on JavaScript patterns as they exist today.

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

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

Narrowing the parent object is only done in specific scenarios, when the property is considered a discriminant for the union. A property is considered as a discriminant property if:

  • The property is a literal type as outlined here #9163
  • The a property of a union type to be a discriminant property if it has a union type containing at least one unit type and no instantiable types as outlined here #27695

如果不遵守这些规则,您最终会得到字段歧视,而不是父对象歧视。