TypeScript by Example

The never Type

Using never for unreachable code, exhaustive switches, and filtering impossible union branches.

never is TypeScript's bottom type: a type with no possible values. It appears when a function never returns, when narrowing exhausts a union, and when type-level filtering removes a branch.

A function that always throws can return never. An assertUnreachable helper uses that fact to make missing cases fail at compile time.

function fail(message: string): never {
  throw new Error(message);
}
 
function assertUnreachable(x: never): never {
  return fail(`Unhandled case: ${JSON.stringify(x)}`);
}
 
type Direction = "north" | "south" | "east" | "west";
 
function move(dir: Direction): string {
  switch (dir) {
    case "north":
      return "moving north";
    case "south":
      return "moving south";
    case "east":
      return "moving east";
    case "west":
      return "moving west";
    default:
      return assertUnreachable(dir);
  }
}

When a teammate adds a new union member, every switch that forgets it points at the never check.

type ApiEvent =
  | { type: "success"; data: { id: string } }
  | { type: "error"; message: string }
  | { type: "loading" };
 
declare function renderUser(user: { id: string }): void;
declare function showError(message: string): void;
declare function showSpinner(): void;
 
function handleEvent(event: ApiEvent): void {
  switch (event.type) {
    case "success":
      renderUser(event.data);
      break;
    case "error":
      showError(event.message);
      break;
    case "loading":
      showSpinner();
      break;
    default:
      assertUnreachable(event);
  }
}

In production

Put assertUnreachable in a shared utils/types.ts. It turns a forgotten branch into a CI failure, which is exactly what you want for events, reducer actions, API statuses, and feature-flag states. The next lesson shows the other major use: returning never to filter members out of a union.

Enjoyed this? Get more software craft delivered to your inbox.