typescript - De ce verificările de exhaustivitate funcționează diferit pentru tipurile union?


Folosesc verificarea exhaustivității așa cum este descrisă în TypeScript Deep Dive

Se pare că verificările de exhaustivitate funcționează diferit pentru tipurile de uniune comparativ cu tipurile care nu sunt tipuri de uniune. De ce??

De exemplu, în codul de mai jos, rețineți că exhaustivenessCheck1 funcționează (nu apare o eroare de tip când nu ar trebui) doar dacă afirmăm că x.kind este never.

Totuși, exhaustivenessCheck2 face corect lucrul doar dacă afirmăm că x este never.

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
    }
}

Link către TypeScript Playground (asigură-te că activezi „verificările stricte pentru null” )

Răspuns #1

Typescript restrânge uniunile atunci când folosești un tip de protecție. Confuzia apare din locul unde se află uniunea.

În Variant1, uniunea este pe membrul kind, așa că TypeScript restrânge acea uniune la never pe ramura default. Aceasta înseamnă că x este încă de tip Variant1, iar kind este încă accesibil pe x, doar că tipul lui kind în acest punct este never

În Variant2, uniunea este aplicată direct parametrului x, astfel încât x este ceea ce este restrâns. Această versiune este numită și uniune discriminată, cu kind fiind discriminatorul. Deoarece toate kinds au fost verificate, în default x va fi restrâns la never și, astfel, accesarea kind devine o eroare.