skip to content

In TypeScript, what does the type `never` represent, and what can you assign to a variable declared `let x: never`?

level: juniorimportance: should knowfreq 50%

answer

  1. the empty set of values
  2. bottom of the assignability lattice
  3. flows out to anything, nothing flows in
  4. impossible branches, throwing functions
  5. erased — no runtime trace

basics

~20 s

never is TypeScript's bottom type: no value has it, so nothing can be assigned to a never variable, while a never value is assignable to every other type. It marks code that cannot be reached.

solid answer

~50 s

`never` is the empty type — the set of values it describes has no members. Two rules follow from that. Nothing is assignable to `never` (not `null`, not `undefined`, not even `any` without an assertion), because you would have to produce a value of a type that has none. And `never` is assignable to *every* type, because an impossible value trivially satisfies any requirement. You see it where the compiler proves something cannot happen: the inferred return type of an arrow function that always throws or loops forever, a branch the checker has narrowed down to nothing, or an intersection like `string & number` that no value can inhabit. Like the rest of the type layer it is erased at compile time — `never` emits no runtime check, so it is a claim about what the checker proved, not a guard.

code

typescript · 14 lines
typescript
declare const n: never;

// never is assignable to every type
const asString: string = n;
const asObject: object = n;
const asNull: null = n;

let x: never;
// x = 1;         // Error: Type 'number' is not assignable to type 'never'
// x = undefined; // Error: undefined is a value; never has none

// an intersection with no possible member reduces to never
declare const impossible: string & number;
const check: never = impossible;

go deeper

for a junior

Be able to say plainly that never means "no value has this type", and that you cannot assign anything — not even undefined — to a never variable. Recognising never in a compiler message is the main goal.

for a middle

Explain the two assignability rules and derive them from the empty-set reading, then name where the compiler produces never: unreachable branches, functions that always throw, and impossible intersections.

for a senior

Show you know never is a compile-time proof with no runtime force. Talk about where you rely on it to make a mistake un-compilable, and where you still need real validation because types are erased.

for a principal

Own the boundary question: which invariants your codebase encodes as never so a wrong change fails the build, and which must be enforced by runtime validation because external data can violate the declared types anyway.

## The one-line definition `never` is TypeScript's **bottom type**: the type whose set of possible values is empty. Every type in TypeScript can be read as a set of values — `string` is the set of all strings, `"a" | "b"` is a two-element set, `unknown` is the set of everything. `never` is the set with nothing in it. No value, at any point, at any time, has the type `never`. Everything else about it is a consequence of that one fact. ## Consequence 1: nothing is assignable to never Assignability in TypeScript is (roughly) the subset relation: `A` is assignable to `B` when every value of `A` is also a value of `B`. Since `never` has no values, no non-empty type can be a subset of it. ```ts let x: never; // x = 1; // Error: Type 'number' is not assignable to type 'never' // x = undefined; // Error — even undefined is a value, and never has none // x = null; // Error ``` This is what makes `never` useful as a *tripwire*: a position typed `never` is one the compiler believes no code can ever reach with a real value, so any assignment to it is an error by construction. That is the machinery behind exhaustiveness patterns, though those belong to their own topic. ## Consequence 2: never is assignable to everything The empty set is a subset of every set, so a `never` value satisfies any requirement at all: ```ts declare const n: never; const a: string = n; // ok const b: object = n; // ok const c: null = n; // ok ``` This is not a special case in the compiler; it falls out of the same subset rule. It also explains why a function whose return type is `never` can be called in any expression position — the value it "returns" can stand in for anything, because it never actually arrives. ## Where never shows up in practice Four places account for nearly every `never` you will meet: 1. **A function that cannot complete.** An arrow function or function expression whose body always throws, or loops forever, infers `never` as its return type. (Function *declarations* behave differently — that asymmetry is worth knowing separately.) 2. **An impossible narrowed branch.** After control-flow analysis has eliminated every member of a union, the variable's type in that unreachable branch is `never`. ```ts declare const s: { kind: "a" } | { kind: "b" }; if (s.kind !== "a" && s.kind !== "b") { const unreachable: never = s; // compiles only because s is now never } ``` 3. **An empty intersection.** `string & number` describes a value that is simultaneously a string and a number; no such value exists, so the type reduces to `never`. 4. **A filtered-away type.** Conditional and mapped types produce `never` to mean "drop this", and `never` disappears from unions (`string | never` is just `string`), so filtering out every member leaves `never` behind. One widely-used application is the `assertNever`-style helper, a function taking a `never` parameter so that a forgotten union member becomes a compile error — the helper itself is its own topic; what matters here is that it works purely because nothing is assignable to `never`. ## never vs the types people confuse it with - **`void`** is not empty. A `void` return means "the value is not meant to be used"; the function still returns (with `undefined`). A `never` return means control never comes back to the caller at all. - **`unknown`** is the *top* type — the set of everything. It is the exact opposite end of the assignability lattice: everything is assignable to `unknown`, and `unknown` is assignable to almost nothing without narrowing. `never` is assignable to everything, and almost nothing is assignable to it. - **`undefined` and `null`** are ordinary single-value types. They have exactly one member each; `never` has zero. - **`any`** disables checking rather than describing an empty set. Assigning an `any`-typed expression to a `never` variable is still rejected. ## It does not exist at runtime Types are erased. A parameter typed `never`, a return type of `never`, a variable annotated `never` — none of them emit a check, a throw, or a byte of JavaScript. If untrusted input reaches a branch the compiler proved impossible (because someone asserted a type, or the data did not match the declared shape), the code in that branch runs with whatever value actually arrived. `never` records a proof about the *declared* types; it does not enforce anything at runtime. When the input crosses a trust boundary, you still validate. ## The mental model to carry into the interview Say "empty set" and derive the rest. Empty set means no value has the type; no value means nothing can be assigned in; empty set is a subset of everything, so it assigns out to anything; and the compiler produces it precisely when it has proved a position cannot hold a value.

  • How is `never` different from `unknown`?
    They are opposite ends of the same lattice. `unknown` is the top type — every value belongs to it, so anything is assignable to `unknown`, but you must narrow before using it. `never` is the bottom type — no value belongs to it, so nothing is assignable to `never`, but a `never` value is assignable to anything. Rough slogan: `unknown` accepts everything, `never` accepts nothing.
  • If nothing is assignable to `never`, how can a variable of type `never` ever be created?
    It cannot be created from a value — only reached as a type. The compiler produces `never` for positions it has proved unreachable: a narrowed-away branch, an empty intersection, or the return of a function that throws. You can also force one with a type assertion, but that is a lie the checker simply accepts, and at runtime the value is whatever was really there.
  • Does annotating something as `never` add any runtime safety?
    No. TypeScript erases types, so a `never` annotation emits nothing — no check, no throw. It only tells the checker that the position is unreachable given the declared types. If an assertion, an `any`, or unvalidated external data breaks those declarations, the supposedly impossible code runs normally. Runtime safety still requires an actual validation step.

never is an empty box that no key opens: you can never put anything into it, but if you somehow had one, it would satisfy any request, because the promise it makes is never tested.

saying these in an interview costs you the question

  • Says never is just another spelling of void
  • Thinks undefined or null can be assigned to never
  • Confuses never with unknown, the top type
  • Claims a never annotation throws or checks at runtime
  • Believes never[] behaves like any[]

context