How 'never' Type Prevents Broken Code in Production
Source: Dev.to
What Is the never Type?
In TypeScript, never represents something that cannot happen.
It is not “returns nothing” or “empty”. It denotes a code path that is impossible to reach.
If a value has type never, it means:
- there is no possible runtime value
- if execution reaches here, something is wrong
Simple Examples
function crash(): never {
throw new Error("Boom");
}
This function never finishes normally.
function infiniteLoop(): never {
while (true) {}
}
Execution can never continue past it, so the return type is never.
The One Rule That Makes never Powerful
never is assignable to every type, but no type (except never itself) is assignable to never.
let x: never;
x = "hello"; // ❌ error
x = 123; // ❌ error
If TypeScript ever sees a real value being assigned to never, the build fails. We’ll use this rule to stop production bugs.
The Setup: A Common React Pattern
Imagine a component that renders UI blocks based on a type field (CMS blocks, feature flags, workflows — very common).
type Block =
| { type: "hero"; title: string }
| { type: "cta"; text: string };
Renderer
function RenderBlock({ block }: { block: Block }) {
switch (block.type) {
case "hero":
return <h2>{block.title}</h2>;
case "cta":
return <div>{block.text}</div>;
}
}
Everything looks fine:
- TypeScript is happy
- CI passes
- No runtime error
The Production Bug (Silent)
A teammate adds a new block:
type Block =
| { type: "hero"; title: string }
| { type: "cta"; text: string }
| { type: "banner"; image: string };
They forget to update RenderBlock. What happens?
- No TypeScript error
- No build failure
- The component returns
undefined→ nothing renders
Production UI is broken silently. This is the worst kind of bug.
Why TypeScript Didn’t Save You
From TypeScript’s point of view, the function might return JSX.Element | undefined, which is technically valid. TypeScript does not assume the switch is exhaustive, so the bug passes.
The Wrong “Fix”
Adding a default case like this:
default:
return null;
still leaves the problem:
- no compiler error
- UI remains broken
- the issue becomes harder to notice
We need TypeScript to fail loudly.
Introducing never
Change one line in the component:
function RenderBlock({ block }: { block: Block }) {
switch (block.type) {
case "hero":
return <h2>{block.title}</h2>;
case "cta":
return <div>{block.text}</div>;
default:
const _exhaustiveCheck: never = block;
return null;
}
}
Result

The key line is:
const _exhaustiveCheck: never = block;
What Changed?
This line tells TypeScript: “If block reaches here, the code is wrong.”
When banner exists, TypeScript sees that block can be { type: "banner" }, which is not assignable to never, producing a compile‑time error. CI fails, and the bug never ships.
The Real Win
It’s not just about exhaustiveness checking; it provides the guarantee:
If the code compiles, all cases are handled.
That’s production safety.
Why This Matters in Real Codebases
The pattern shines when:
- CMS schemas evolve
- Multiple teams touch the same union types
- UI rendering depends on configs or API responses
- Reducers or workflows grow over time
Anywhere types change faster than implementations, never protects you.
Mental Model
never means: “This code path must not exist.”
If TypeScript ever proves it can exist, the build fails — by design.
Conclusion
- TypeScript does not guarantee exhaustive handling by default.
- React components can silently break in production.
neverturns missing cases into compiler errors.
One line prevents an entire class of bugs:
const _exhaustiveCheck: never = value;
Use it wherever silence is dangerous.