Discriminated unions

Discriminated unions in TypeScript can help model different states in software. You can create a discriminated union by creating a union of types with a literal member that is common among all the types. This literal member will distinguish among the different union types.

type StartedCourse = {
    started: true;
    lastInteractionTime: Date;
};

type UnstartedCourse = {
    started: false;
};

type Course = StartedCourse | UnstartedCourse;

In the above example, started is the literal member that is the discriminator. If started is true, then we know the type is a StartedCourse, else if it is false then we know it’s an UnstartedCourse. By being able to determine which of the types in the union the value is, we are able to access additional properties safely.

const myCourse: Course = {
    started: true,
    lastInteractionTime: new Date(2022, 1, 29),
};

myCourse.lastInteractionTime.getFullYear();
// 2022

The above is valid because the compiler knows that myCourse is a StartedCourse from it’s started value.

const myCourse: Course = {
    started: false,
};

myCourse.lastInteractionTime.getFullYear();
// type error

The above is invalid because the compiler knows that myCourse is an UnstartedCourse and that type does not have the lastInteractionTime property.

The example in this post was taken from the Everyday TypeScript course from Execute Program. I have enjoyed and benefited in my TypeScript understanding from this course and highly recommend it to others.