Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Discriminated unions with booleans in Typescript

To keep track of an initializing/initialized object I want to create a discriminated union with a boolean. And so I wrote the following code:

interface InitializingThing extends BaseThing {
    initialized: false;
    value: undefined;
}

interface InitializedThing extends BaseThing {
    initialized: true;
    value: number;
}

type Thing = InitializingThing | InitializedThing;

const thing: Thing = { initialized: false, value: undefined };
console.log(thing);

getThing().then((value: number) => {
    thing.value = value;
    thing.initialized = true;
}).then(() => {
    if (!thing.initialized) {
        return;
    }
    console.log(15 + thing.value);
});

(see on the Typescript playground )

However this gives the errors

Type 'number' is not assignable to type 'undefined'.(2322)
Type 'true' is not assignable to type 'false'.(2322)

I can see from hovering over the console.log(thing) that the type is InitializingThing instead of Thing! Which seems to be the root of the problem, but I'm not sure why the TS compiler would do this.

like image 797
Stuart K Avatar asked Jan 24 '26 15:01

Stuart K


1 Answers

Your code in principle works fine. However, in above example, TS actually is a bit "too smart" with its control flow analysis. In following variable assignment

const thing: Thing = { initialized: false, value: undefined };

, the compiler narrows the type of thing to InitializingThing by interpreting its initialiser. thing is also assumed to keep this type, since the variable is readonly/const. That is why an error is triggered for the re-assignments in the then clause.

If you enforce thing to really be of type Thing, given sample will compile again:

const thing: Thing = { initialized: false, value: undefined } as Thing; // note the cast here
// or 
let thing: Thing = { initialized: false, value: undefined }; // switch to mutable `let`

Playground

like image 115
ford04 Avatar answered Jan 26 '26 17:01

ford04