Search code examples
typescripttypescript-types

Discriminated union not working when narrowing after array access


Given these two types:

type Prospect = {
    name: string;
}

type Client = {
    name: string;
    account: string;
}

Let's create a union where kind acts as the discriminator:

type Union = 
| {kind: "prospect", persons: Prospect[]} 
| {kind: "client", persons: Client[]};

const {persons, kind} = {} as Union

Type narrowing works as expected when accessing persons.

if (kind === "client") {
    persons.map(person => { // person: Client
        person.account // All good
    })
}

It, however, fails to work after accessing an individual element from persons.

const person = persons[0]
if (kind === "client") { // person: Prospect | Client
    person.account // Error
}

How can I achieve type narrowing after I have already accessed an array? I could add a kind field to Prospect and Client but that seems redundant given that persons is always an array of Prospect or Client and not a mix of both.


Solution

  • You can move person access inside type guard:

    if (kind === "client") { // person: Prospect | Client
        const person = persons[0]
        person.account // Ok
    }
    

    In your case of

    const person = persons[0]
    if (kind === "client") { // person: Prospect | Client
        person.account // Error
    }
    

    Typescript cannot guess that person/persons[0] didn't mutate and change type between assignment and type guard, so it cannot safely narrow type.