dwjohnston
dwjohnston

Reputation: 11803

Discriminated union check on existence of a field?

What I want to do is pretty simple:

I have an type Foo, but there's cases where the Foo might be empty, so we have a union type with an empty object. (The use case here is actually with Redux, which doesn't let you have a redux state initalised as undefined).

interface Foo {
    id: string; 
    data: string; 
}

type PotentialFoo = Foo | {}; 

Later, when I'm using one of these PotentialFoos, an easy way to check if it's a Foo or not, is to check if one of the fields exist.

But the TypeScript compiler doesn't like this.

const itemA: PotentialFoo = {
    id: "foo", 
    data :"data", 
}

const itemB: PotentialFoo = {}; 

function someFunct(item: PotentialFoo) {
    if (item.id) { //  Property 'id' does not exist on type '{}'.

        //handle it as a Foo. 
    }
}

Is there a way to to otherwise allow a discrimination here, like TypeScript allows in other cases?

Upvotes: 3

Views: 1543

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249606

The simplest solution in this case is to use an in check as a type guard:


function someFunct(item: PotentialFoo) {
    if ('id' in item) {

        item.data // ok
    }
}

Play

Upvotes: 8

dwjohnston
dwjohnston

Reputation: 11803

One solution is to use a TypeScript type predicate, which is essentially to create a boolean function to determine the type, like you have here, but gives better compiler hints to TypeScript.

const itemA: PotentialFoo = {
    id: "foo", 
    data :"data", 
}

const itemB: PotentialFoo = {}; 

function isFoo(item: PotentialFoo) : item is Foo {
    return !!(item as Foo).id; 
}


function someFunct(item: PotentialFoo) {
    if (isFoo(item)) {
        //handle it as a Foo. 
    }
}

Upvotes: 2

Related Questions