Lesson 24 of 80
The type with no possible values — used less to describe data and more as a trap the compiler can catch you in in when a switch statement forgot a case.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
In the exhaustiveness-check pattern, why does `const _exhaustive: never = state;` in the default branch cause a compile error only when a case was forgotten?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
never is the type of a value that can't exist — a function that always throws, or always loops forever, returns never (it never actually produces a value to return). On its own that sounds like a curiosity; combined with a discriminated union, it becomes a real safety net.
type State = { status: "loading" } | { status: "success"; data: string } | { status: "error"; message: string };
function render(state: State): string {
switch (state.status) {
case "loading": return "Loading...";
case "success": return state.data;
case "error": return state.message;
default:
const _exhaustive: never = state; // ❌ compile error if a case was missed
throw new Error(`Unhandled status`);
}
}
After handling every known status value, TypeScript narrows state in the default branch down to whatever's left over — if every case was handled, nothing is left, so state's type there is never. Assigning it to a variable explicitly typed never only compiles if that's true. Add a new state to the union later and forget to handle it in the switch, and this line becomes a compile error, not a runtime surprise.
switch(state.status) handles 'loading', 'success', 'error' — every case in the union, one by one.
never is different from void. void means "returns, but the return value is meaningless" (like most event handlers); never means "never returns at all" (throws, infinite loop, or process exit).never in it just drops that member — string | never simplifies to plain string. This is exactly why never is the natural "filter this branch out" result in a conditional type's false branch.never is assignable to every type, but nothing (except never itself) is assignable to never. It's the "bottom type" in TypeScript's type hierarchy — the opposite extreme from any, which is assignable both ways.The exhaustiveness-check pattern is a genuinely impressive, senior-level thing to volunteer unprompted when discussing discriminated unions — it shows you're thinking about how the codebase stays correct as it evolves, not just how to model the current state.