DEV Community

Cover image for The `Never` type - the typeScript feature most developers ignore… Until It Breaks Their Code
Md. Mehedi Hasan
Md. Mehedi Hasan

Posted on • Edited on

The `Never` type - the typeScript feature most developers ignore… Until It Breaks Their Code

TypeScript is known for catching bugs before runtime.

But one of its most underrated features is the never type, especially for exhaustive type checking.

Most developers know void.

Fewer understand never.

Even fewer actually use it correctly.

Yet this tiny feature can prevent entire classes of bugs in production.


🔹 void vs never (Quick Reality Check)

void   function finishes normally (no return value)
never  function never finishes normally (throws or infinite loop)
Enter fullscreen mode Exit fullscreen mode

Example:

function logMessage(): void {
  console.log("Hello");
}

function throwError(): never {
  throw new Error("Something went wrong");
}
Enter fullscreen mode Exit fullscreen mode

💥 The Real Problem (Missing Exhaustiveness)

We define a union type:

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number };
Enter fullscreen mode Exit fullscreen mode

And write a function:

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;

    case "rectangle":
      return shape.width * shape.height;

    case "triangle":
      return (shape.base * shape.height) / 2;

    default:
      return 0;
      // ❌ Problem: TypeScript assumes this is fine
      // ❌ But we are NOT ensuring all cases are handled
  }
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Hidden Bug (When Type Changes)

Later, a new requirement comes:

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number }
  | { kind: "square"; side: number }; // NEW
Enter fullscreen mode Exit fullscreen mode

Now our function is silently broken:

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;

    case "rectangle":
      return shape.width * shape.height;

    case "triangle":
      return (shape.base * shape.height) / 2;

    default:
      return 0;

      /*
        ❌ PROBLEM:
        - "square" is NOT handled
        - No TypeScript error
        - No warning
        - This will fail silently in production
      */
  }
}
Enter fullscreen mode Exit fullscreen mode

🧠 The Fix: never (Exhaustive Checking)

We create a helper:

function assertNever(value: never): never {
  throw new Error(`Unexpected value: ${value}`);
}
Enter fullscreen mode Exit fullscreen mode

🔥 Safe Version (WITH never — Fully Exhaustive)

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number }
  | { kind: "square"; side: number }; // NEW SHAPE ADDED

function assertNever(value: never): never {
  throw new Error(`Unexpected value: ${value}`);
}

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;

    case "rectangle":
      return shape.width * shape.height;

    case "triangle":
      return (shape.base * shape.height) / 2;

    default:
          return assertNever(shape);
      /*
        🔥 This is where magic happens

        If ANY new shape is added and NOT handled,
        TypeScript will throw an error here.
      */

  }
}
Enter fullscreen mode Exit fullscreen mode

🚨 What Happens Now?

If someone adds:

{ kind: "square"; side: number }
Enter fullscreen mode Exit fullscreen mode

TypeScript shows:

❌ Argument of type '"square"' is not assignable to parameter of type 'never'
Enter fullscreen mode Exit fullscreen mode

🧠 Meaning of This Error

“You said you handled all cases… but you didn’t.”


📊 Before vs After Mental Model

❌ Without never

Type changes
   ↓
Switch not updated
   ↓
No TypeScript error
   ↓
💥 Bug reaches production
Enter fullscreen mode Exit fullscreen mode

✅ With never

Type changes
   ↓
Switch not updated
   ↓
TypeScript compile error
   ↓
🛑 Bug stopped before runtime
Enter fullscreen mode Exit fullscreen mode

💡 Why This Matters in Real Projects

This pattern is extremely useful in:

  • 🧾 Payment systems
  • 📦 Order status flows
  • 👤 Role-based access control
  • 🌐 API response handling
  • 🎯 UI state machines

🚀 Final Thought

never is not just a TypeScript trick.

It is a compile-time safety guarantee for your business logic.

If you're not using exhaustive checking yet, you're only partially using TypeScript.


🧠 One-line summary

“If you're not using never for exhaustive checks, TypeScript is not fully protecting your logic.”

Top comments (0)