Exhaustiveness Checking with never

Make the Compiler Remind You

After all cases of a union are handled, the remaining type is never. Assigning the value to a never variable in the default branch turns "I forgot a case" into a compile error.

Example

function assertNever(x: never): never {
  throw new Error("Unhandled case: " + JSON.stringify(x));
}

type Shipping = { mode: "standard" } | { mode: "express" } | { mode: "pickup"; store: string };

function shippingFee(s: Shipping, cartTotal: number): number {
  switch (s.mode) {
    case "standard": return cartTotal >= 499 ? 0 : 40;
    case "express": return 99;
    case "pickup": return 0;
    default: return assertNever(s);
  }
}

console.log(shippingFee({ mode: "standard" }, 300));
console.log(shippingFee({ mode: "express" }, 300));
console.log(shippingFee({ mode: "pickup", store: "Koramangala" }, 300));

Output

40
99
0

Adding a New Case Later

Example

Someone adds "drone" delivery but forgets the switch. The compiler points straight at it (TS2345: { mode: "drone" } is not assignable to never).

function assertNever(x: never): never {
  throw new Error("Unhandled case: " + JSON.stringify(x));
}

type Shipping = { mode: "standard" } | { mode: "express" } | { mode: "drone" };

function shippingFee(s: Shipping): number {
  switch (s.mode) {
    case "standard": return 40;
    case "express": return 99;
    default: return assertNever(s);
  }
}

Compiler output (tsc)

Line 11: error TS2345: Argument of type '{ mode: "drone"; }' is not assignable to parameter of type 'never'.