Most production bugs don’t come from syntax errors.
They come from valid code paths that nobody noticed.
TypeScript helps a lot — but there’s one class of bugs it does not catch by default, especially in React codebases that evolve over time.
This article shows:
- a real React bug that silently ships to production
- why TypeScript doesn’t warn you
- how the
nevertype turns that bug into a compile-time error
What Is the never Type?
In TypeScript, never represents something that cannot happen.
Not “returns nothing”.
Not “empty”.
But this:
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.
That’s why the return type is never.
The One Rule That Makes never Powerful
This rule is the key:
neveris assignable to every type,
but no type is assignable tonever(exceptneveritself).
Example:
let x: never;
x = "hello"; // ❌ error
x = 123; // ❌ error
If TypeScript ever sees a real value being assigned to never,
it immediately fails the build.
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 <h1>{block.title}</h1>;
case "cta":
return <button>{block.text}</button>;
}
}
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
- No runtime crash
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 - that is technically valid
- TypeScript does not assume your
switchis exhaustive
So the bug passes.
The Wrong “Fix”
You might add a default:
default:
return null;
This makes things worse:
- still no compiler error
- still broken UI
- now even harder to notice
We need TypeScript to fail loudly.
Introducing never
Now we change one line.
function RenderBlock({ block }: { block: Block }) {
switch (block.type) {
case "hero":
return <h1>{block.title}</h1>;
case "cta":
return <button>{block.text}</button>;
default:
const _exhaustiveCheck: never = block;
return null;
}
}
*The result: *
This line is the key:
const _exhaustiveCheck: never = block;
What Changed?
This line tells TypeScript:
“If
blockreaches here, this code is wrong.”
When banner exists, TypeScript now sees:
-
blockcan be{ type: "banner" } -
banneris not assignable tonever
Compile-time error.
CI fails.
The bug never ships.
The Real Win
This is not about exhaustiveness checking.
It’s about this guarantee:
If the code compiles, all cases are handled.
That’s production safety.
Why This Matters in Real Codebases
This 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
nevermeans:
“This code path must not exist.”
If TypeScript ever proves that it can exist,
your 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 where silence is dangerous.

Top comments (0)