Everyday TypeScript: Never
Welcome to the Never lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
Some functions never return. For example, a function that always throws an exception never returns.
>
function throws() {throw new Error('oh no');}throws();Result:
What should this function's return type be? TypeScript will infer it to have a return type of
void. But that doesn't fully capture what's happening.The
voidtype is for functions that begin, do some work, and finish, but don't return a value. Our function above is different: if we call it, then the code after that call will never execute at all.Fortunately, there's a TypeScript type for that:
never.>
function throws(): never {throw new Error('oh no');}throws();Result:
The
nevertype is a bit strange. It's assignable to any other type. The next example is like the example above, except that it assigns theneverreturn value to anumbervariable. The function still throws an exception, so the answer here is the same as above.(When code will result in an error thrown at runtime, you can answer with
error.)>
function throws(): never {throw new Error('oh no');}const n: number = throws();Result:
Error: oh no
At first, it seems like
neveris acting likeany, because we're allowed to assign theneverto anumber(or astring, or anything else). But unlikeany, this is safe!The compiler knows that our
throwsfunction never returns, so it also knows that nothing will ever be assigned to thenvariable. Types exist to ensure that the data is correct at runtime. If the compiler knows that the assignment will never happen at runtime, then the variable's type doesn't matter.However, we can't assign any other type to
never. That would break the rule ofnever: it never actually holds a value at runtime. Trying to assign any value to aneveris always a type error.(When a code example contains a type error, you can answer with
type error.)>
const aNever: never = 5;aNever;Result:
type error: Type '5' is not assignable to type 'never'.
Absolutely nothing can be assigned to a
never; not even anany!>
const anAny: any = 5;const aNever: never = anAny;aNever;Result:
type error: Type 'any' is not assignable to type 'never'.
If a function always throws, but has no explicit return type, then TypeScript will infer a
voidreturn type. That's not bad, but it's better to explicitly mark it asnever, which gives TypeScript a bit more information to work with.For example, suppose that a function calls another function, returning its return value. If the inner function returns
never, TypeScript will correctly infer that the outer function's return type is alsonever.>
function throws(): never {throw new Error('oh no');}function alsoThrows() {return throws();}const n: number = alsoThrows();Result:
That code compiled, which tells us that
alsoThrowshas a return type ofnever. (If it returnedvoid, then theconst n: number = alsoThrows()line would type error, because we can't assign avoidto anumber.)However, this type inference doesn't rely on any special TypeScript behavior around
never. It's just normal TypeScript type inference:alsoThrowsreturnsthrows(), so it has the same return type asthrows.Here's a code problem:
First, run this code to see that it doesn't error. Then change its return type to
never. That will cause a type error, because the function does return. TypeScript won't allow aneverfunction to return a value.function f(): never {return 5;}- Goal:
type error: Type '5' is not assignable to type 'never'.
- Yours:
type error: Type '5' is not assignable to type 'never'.
TypeScript also won't let a
neverfunction return conditionally (for example, inside anif). If there's any way for a function to return a value, then its return type can't benever; that wouldn't make sense.(The next example has a type error for exactly that reason.)
>
function forcePositive(x: number): never {if (x > 0) {return x;} else {throw new Error('Number was negative!');}}Result:
type error: Type 'number' is not assignable to type 'never'.
The
nevertype applies to more than just exception-throwing functions. For example, a function with an infinite loop can (and should!) have a return type ofnever.Unfortunately, we can't show a live example of that here because it would never terminate. However, we can show the opposite: a function that looks kind of like an infinite loop, and claims to be a
never, but will sometimes return. That's a type error.>
function f(): never {while (true) {if (Math.random() < 0.000001) {break;}}}Result:
type error: A function returning 'never' cannot have a reachable end point.
Finally, a quick summary of
voidandnever.voidmeans that the function eventually terminates, but it doesn't return any value. The function either has noreturnstatement, or it has a barereturn;statement with no value after it. Thevoidtype isn't assignable to anything because it represents the absence of a value.nevermeans that the function never terminates normally. It might throw an exception or go into an infinite loop. If we're in a server-side system like Node, it might callprocess.exit()to terminate the process. One way or another, execution never reaches the end of the function. Thenevertype is assignable to anything because the compiler knows that the assignment will never actually happen at runtime.