为什么详尽检查对联合类型的作用不同?

Why do exhaustiveness checks work differently for union types?

我正在使用 TypeScript Deep Dive

中所述的详尽检查

与非联合类型的类型相比,联合类型的详尽性检查似乎有所不同。为什么??

例如,在下面的代码中,请注意 exhaustivenessCheck1 仅在我们断言 x.kindnever 时有效(在不应输入错误时不输入错误)。

然而,只有当我们断言 xnever 时,exhaustivenessCheck2 才会做正确的事情。

type Variant1 = {
    kind: 1 | 2
}

type Variant2 = {
    kind: 1
} | {
    kind: 2
}

const x: Variant1 = { kind: 1 };


function exhaustivenessCheck1(x: Variant1) {
    switch (x.kind) {
        case 1:
        case 2:
            break;
        default:
            const _x: never = x.kind; // OK
            const _y: never = x; // Error
    }
}

function exhaustivenessCheck2(x: Variant2) {
    switch (x.kind) {
        case 1:
            break;
        case 2:
            break;
        default:
            const _x: never = x.kind; // Error
            const _y: never = x; // OK
    }
}

TypeScript Playground link (be sure to enable "strict null checks")

当您使用类型保护时,Typescript 会缩小并集。混乱来自工会的位置。

Variant1 中,联合位于 kind 成员上,因此打字稿将该联合缩小到 default 分支上的 never。这意味着 x 仍然是 Variant1 类型,并且 kind 仍然可以在 x 上访问,只是此时 kind 的类型是 never

Variant2 中,并集在 x 参数本身上,因此 x 是变窄​​的。此版本也称为以 kind 为鉴别器的可鉴别联合。由于所有 kinds 都已检查,因此 default x 将缩小到 never,因此访问 kind 会出错。