void and never
Calling a function is like exchanging parcels. You send the arguments, and the function sends back a return parcel: the result. The result type goes after the parentheses: ): number promises that a number comes back.
Some functions have nothing to send back. One prints a line on the screen and finishes its work: the return parcel arrives empty. Another always throws an error: the return parcel never arrives. TypeScript has the types void and never for these two cases.
void: an empty reply
The type void as a function's result means "there is no useful result". The function does its work and returns nothing:
function logLine(text: string): void {
console.log("> " + text);
}
const result = logLine("start"); // > start
console.log(result); // undefinedLet's walk through the example line by line:
logLineis declared with the result typevoid.- The call
logLine("start")prints> start. resultreceives whatever the function returned. The function has noreturn, so JavaScript returnsundefined.console.log(result)printsundefined.
So the empty parcel does arrive, and it holds undefined. The type void does not say "this is undefined"; it says "do not use the result". The editor's tooltip shows the type void for result.
You have met void already, even if you did not notice it. Hover over log in console.log: the tooltip shows : void after the parentheses. The method prints text and returns no useful result.
You may leave out the void annotation: if a function has no return with a value, TypeScript infers void on its own. With the annotation, though, tsc checks that the function really returns nothing:
function logLine(text: string): void {
console.log("> " + text);
return text.length; // ✗
}error TS2322: Type 'number' is not assignable to type 'void'.never: no reply at all
The type never means "the function never finishes normally". Most often such a function always throws an error:
function fail(message: string): never {
throw new Error(message);
}
function toPercent(part: number, whole: number): number {
if (whole === 0) {
return fail("whole is zero");
}
return (part / whole) * 100;
}
console.log(toPercent(1, 4)); // 25Let's walk through the example:
failthrows anErrorand never reaches the end of its body, so its result isnever.toPercentpromises anumber, yet one branch returnsfail(...).tscaccepts this: a value of typenevernever appears, so it fits every type.- The call
toPercent(1, 4)takes the second branch and prints25.
The type never also suits a function with an endless while (true) loop and no break: it does not return either. But for a declaration of such a function, or of one that only throws, TypeScript itself infers void rather than never. That is why you write never explicitly.
Here the parcel picture stops working. The return parcel from fail never arrives, but the thrown error is not lost: a try/catch further up the chain of calls can catch it.
Interviewers often ask: "How does void differ from never?"
void: the function finishes but returns no useful result, and the calling code getsundefined;never: the function does not finish at all; it throws an error or runs forever.
Common mistake
never was written to mean "returns nothing":
function logLine(text: string): never { // ✗
console.log(text);
}error TS2534: A function returning 'never' cannot have a reachable end point.The message says that a function of type never must not reach the end of its body. But logLine does: it prints a line and finishes. Such a function needs void.
Key points
voidas a result means "no useful result": the function finishes, and the calling code getsundefined.- With a
voidannotation the compiler does not let the function return a value. nevermeans the function does not finish normally: it always throws an error or runs forever.- A function that reaches the end of its body cannot be typed
never:tscanswers with errorTS2534.