TypeScript · Types that pay for themselves

How do you get exhaustiveness checking on a union?

MediumexhaustivenessneverswitchUpdated Oct 2026
Quiz this track

Short answer

Exhaustiveness checking uses never to prove every union member is handled. If a new variant is added, the assignment to never fails at compile time, pointing to the missing case.

A switch over a union is exhaustive when every member has a case. The compiler can prove that only if the default branch receives a value of type never. A small assertNever helper turns that proof into a compile-time error when a new variant appears.

ts
type Status = "idle" | "loading" | "success" | "error";

function assertNever(value: never): never {
  throw new Error("Unhandled status: " + String(value));
}

function label(status: Status): string {
  switch (status) {
    case "idle":
      return "Idle";
    case "loading":
      return "Loading";
    case "success":
      return "Done";
    case "error":
      return "Failed";
    default:
      return assertNever(status);
  }
}

Why it works

  • After every case, the remaining type is never
  • Passing never to a parameter typed never is valid
  • Adding a new member makes the remaining type non-never and breaks the call
  • The error points at the missing case, not at the new member
  • The same pattern works with if-else chains and reducer actions

Exhaustiveness checking is valuable in reducers, message handlers and any switch over a union that is expected to grow. It turns a silent runtime fallthrough into a build failure. The helper must actually throw at runtime, because the compiler only checks the types.

Do not add a default case that returns a fallback value if you want exhaustiveness. A fallback accepts the new member silently and defeats the check.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1Why does the remaining type become never?
  2. 2What happens when a new union member is added?
  3. 3Can you use exhaustiveness checking with if-else?
  4. 4Why is a fallback default harmful here?

Say it in the interview

Exhaustiveness checking makes the compiler prove that every union member is handled. I route the default branch into a function that accepts never; if a new variant appears, the call no longer type-checks. That turns a runtime fallthrough into a build error. I use it in reducers, message handlers and anywhere a union is likely to grow.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live TypeScript mock interview and get scored feedback on depth, clarity and confidence.