Discriminated Unions

Normal Union

type State = {
    status: "loading" | "success" | "error";
    error?: Error;
    data?: Array<{ id: number; name: string }>;
};

Example 1 - These are type-safe:

const state1: State = {
    status: "loading",
};

const state2: State = {
    status: "success",
    data: [{ id: 1, name: "Jane" }, { id: 2, name: "John" }],
};

const state3: State = {
    status: "error",
    error: new Error("The task failed successfully"),
};

Example 2 - These are also type-safe but logically incorrect:

// The data has been fetched, but the status is still "loading"
const state4: State = {
    status: "loading",
    data: [{ id: 1, name: "Jane" }, { id: 2, name: "John" }],
};

// The status is "success", but there is an error present
const state5: State = {
    status: "success",
    error: new Error("The task failed successfully"),
};

// The status is "error", but there is no error object
const state6: State = {
    status: "error",
};

Discriminated Union

Invalid combinations like in Example 2 are now caught by the type system:

type State =
    | { status: "loading" }
    | { status: "success"; data: Array<{ id: number; name: string }> }
    | { status: "error"; error: Error };

📚 References