Lesson 11 · Module 1.6 — Core type vocabulary

The never type — exhaustiveness, and the key that disappears

Here it is in one line: the never type has two behaviors. In the value world it powers exhaustive switch checks; in the type world it makes a mapped-type key vanish (because string | never = string). Same type, two faces — and both show up constantly in real code.

never is the empty type

Read never as “a type with no possible values.” Nothing is assignable to it (no value can be one). That single fact explains both behaviors below.

Face 1 (value world): exhaustive switch checking

A classic pattern — assign the “impossible” branch to a never so the compiler forces you to handle every case:

type Shape = { kind: "circle" } | { kind: "square" };

function handle(shape: Shape): string {
  switch (shape.kind) {
    case "circle": return "circle";
    case "square": return "square";
    default:
      const _exhaustive: never = shape;  // ✅ only compiles if no case is left
      return _exhaustive;
  }
}

By the default branch, TypeScript has narrowed shape down to never — there’s nothing left it could be. Assigning it to _exhaustive: never succeeds. But the day someone adds { kind: "triangle" } to Shape, that branch can now be reached with a real value, shape is no longer never, and the assignment fails to compile — a free reminder to handle the new case. Read const _x: never = shape as “prove to me nothing can reach here.”

Face 2 (type world): a key that disappears

The other behavior worth flagging: in a union, never contributes nothing — string | never is just string. Key remapping in a mapped type exploits this to drop a key:

type User = { name: string; age: number; password: string };

type PublicUser = {
  [K in keyof User as K extends "password" ? never : K]: User[K]
};
// Result: { name: string; age: number }   — "password" remapped to never, so it's gone

Read the as clause as “rename each key K to… itself, unless it is "password", in which case rename it to never.” And a key of type never is filtered out of the result — exactly because never vanishes from a union. That is how mapped types delete fields.

The flip side: the dreaded “not assignable to never

Here is the error that confuses everyone. Picture an object whose two properties have different types, and a function that writes to one of them through a key that could be either one:

const counts = { cats: 0, dogs: "lots" };   // cats: number, dogs: string

function set(key: "cats" | "dogs", value: number | string) {
  counts[key] = value;   // ❌ Type 'string | number' is not assignable to type 'never'
}

Reading counts[key] is fine — you get string | number back. Writing is the trap. Because key might be "cats" (which needs a number) or "dogs" (which needs a string), the value you assign has to be valid for both at once — a number and a string. No value is both, so that combined type is never (the empty type from the top of this lesson). Nothing fits, and TS says “not assignable to never.”

Read “not assignable to never” as “the compiler narrowed this slot down to the empty type, so nothing can go in.” Same never as before — just showing up on the write side this time.

Read it this way

never = the type with no values. Assigned to a value (const _x: never = shape) → an exhaustiveness guard. Produced by a mapped-type key → that key is dropped. Seen in an error → the compiler narrowed something to nothing.

Check yourself

Recall, don’t re-read. (Answers reveal on click.)

Why does const _x: never = shape in a default branch enforce exhaustiveness?
In a mapped type, remapping a key to never via as causes that key to…
An error saying “Type 'string' is not assignable to type 'never'” usually means…

Primary source

For the exhaustiveness pattern, the handbook page is Narrowing — Exhaustiveness Checking. For the key-dropping behavior, see Mapped Types — Key Remapping via as. Best way to feel it: paste the Shape switch into the TS Playground, add a third kind, and watch the _exhaustive line light up red.

Try it yourself

Hit a stray never in an error and can’t tell which face it is? Ask yourself “value-world guard, type-world drop, or a narrowing dead-end?” This closes Module 1 — next is Module 2 on generics, where these vocabulary pieces start composing.