never
Marks values that never occur — thrown functions, empty unions, and exhaustive switch checks.
function error(message: string): never- Added in TypeScript 2.0
Return value
neverNothing. A function annotated never must not have a reachable end point.
Errors
Assigning any value other than never to a never-typed target is a compile error: "Type 'X' is not assignable to type 'never'."
Changelog
- PHP 2.0never introduced. It is assignable to every type; no type except never is assignable to it.
- PHP 3.7Assertion functions and never-returning call signatures participate in control flow analysis, so a call to a never-returning function ends the flow of the enclosing block.
- PHP 4.xConditional types distribute over unions, so a branch resolving to never is removed from the resulting union — the mechanism behind Exclude and NonNullable.
never is the bottom type: the type of values that never occur. No value has it, so you can never construct one, and you can never log one. It is assignable to every other type, and nothing except never is assignable to it.
The three places it shows up
- Functions that do not return. A function that always throws or loops forever is inferred
never. Annotating a helperfunction fail(msg: string): neverlets callers use it as an expression and tells control flow analysis that everything after the call is unreachable. - Narrowing to nothing. When every member of a union has been eliminated by type guards, the remaining type is
never. - Exhaustiveness. Assigning the narrowed value to a
nevervariable in adefaultbranch compiles only while every case is handled. Add a variant to the union later and that line becomes an error naming the case you forgot — a compile-time reminder that no test would have given you.
never also disappears from unions (string | never is string), which is how conditional types such as Exclude filter members out.
Example
type Shape =
| { kind: "circle"; r: number }
| { kind: "square"; side: number }
| { kind: "tri"; base: number; h: number }; // added later; area() not updated
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.r ** 2;
case "square": return shape.side ** 2;
default: {
// tsc: Type '{ kind: "tri"; base: number; h: number; }'
// is not assignable to type 'never'.
const exhaustive: never = shape;
throw new Error(`unhandled kind: ${(exhaustive as Shape).kind}`);
}
}
}
console.log(area({ kind: "square", side: 3 }));When to use it
- Forcing a compile error when a new member is added to a discriminated union but a switch is not updated
- Declaring a `fail()` or `assertNever()` helper that always throws so callers can use it in an expression position
- Filtering members out of a union in a conditional type by resolving the unwanted branch to never
Frequently asked questions
What is the difference between never and void in TypeScript?
`void` means the function returns, but with no useful value; `never` means it does not return at all. A `void` function reaches its end point and yields `undefined`, so calling it and continuing is fine. A `never` function always throws or loops forever, so TypeScript treats the code after the call as unreachable and narrows types accordingly.
How do I get an error when I forget a case in a switch?
Add a `default` branch that assigns the switch subject to a variable typed `never`. While every union member is handled, the subject has narrowed to `never` and the assignment compiles; add a new member and tsc reports "Type 'X' is not assignable to type 'never'", naming the case you missed. Pair it with an `assertNever(x: never): never` helper that throws at runtime.
Why am I getting "Type 'string' is not assignable to type 'never'"?
Because TypeScript has narrowed the target to `never` — usually an exhaustiveness check whose union grew, or an array or object it inferred as `never[]` / `{}` from an empty initialiser. Either handle the new union member, or annotate the empty literal explicitly, for example `const items: string[] = []` instead of `const items = []`.
More examples
A never-returning helper ends control flow
Prints `8080`, `PORT is not set`, `PORT is not an integer: eighty`. Because `fail` is annotated `never`, TypeScript knows the first `if` cannot continue, so `raw` narrows to `string` on the next line without an `else`. It also lets `fail(...)` sit in the `number` return position — `never` is assignable to every type.
function fail(message: string): never {
throw new Error(message);
}
function port(raw: string | undefined): number {
if (raw === undefined) fail("PORT is not set");
// raw is string here: the call above cannot fall through
const n = Number(raw);
return Number.isInteger(n) ? n : fail(`PORT is not an integer: ${raw}`);
}
console.log(port("8080"));
try { port(undefined); } catch (e) { console.log((e as Error).message); }
try { port("eighty"); } catch (e) { console.log((e as Error).message); }never as an empty union and a filter
Prints `[ 'live', 'archived' ] 0` and then `unexpected value: "draft"`. `Exclude` works by mapping the excluded member to `never`, which is then dropped from the union. `never[]` is a legitimate type but can only ever hold the empty array, and `assertNever` is the runtime half of an exhaustiveness check.
type Status = "draft" | "live" | "archived";
// Conditional types distribute; the unwanted branch becomes never and vanishes.
type Public = Status extends infer S ? (S extends "draft" ? never : S) : never;
type AlsoPublic = Exclude<Status, "draft">; // "live" | "archived"
const shown: AlsoPublic[] = ["live", "archived"];
const empty: never[] = []; // the only array a never element type permits
function assertNever(x: never): never {
throw new Error(`unexpected value: ${JSON.stringify(x)}`);
}
console.log(shown, empty.length);
try { assertNever("draft" as never); } catch (e) { console.log((e as Error).message); }
Discussion