In TypeScript, you can annotate a function as returning void
:
function fn1(): void {
// OK
}
function fn2(): void {
// Error
return 3;
}
You can also annotate a function to return undefined
:
function fn3(): undefined {
// OK
return;
}
function fn4(): undefined {
// Error
return 3;
}
So it seems that if you call a function returning void
, you'll always get back the value undefined
. Yet you can't write this code:
function fn5(): void {
}
let u: undefined = fn5(); // Error
Why isn't void
just an alias for undefined
? Does it need to exist at all?
void
has special meaning in function return types, and is not an alias for undefined
. Thinking of it this way is very wrong. Why?
The intent of void
is that a function's return value will not be observed. This is very different from will be undefined
. It's important to have this distinction so that you can properly describe functions like forEach
. Let's consider a freestanding version of Array#forEach
, written with undefined
instead of void
in the callback return position:
declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void;
If you tried to use this function:
let target: number[] = [];
forEach([1, 2, 3], el => target.push(el));
You'd get an error:
Type "number" is not assignable to type "undefined"
This is a correct error - you said you wanted a function that returned the value undefined
, but you actually provided a function that returned the value number
because that's what Array#push
returns!
Using void
instead means that forEach
promises not to use the return value, so it can be called with a callback that returns any value
declare function forEach<T>(arr: T[], callback: (el: T) => void): void;
let target: number[] = [];
// OK
forEach([1, 2, 3], el => target.push(el));
Why not just use any
? If you're actually the one implementing forEach
, you really don't want that - having an any
floating is a dangerous thing that can defeat typechecking very easily.
The corollary to this is that if you have some function expression whose return type is void
, you cannot say with any certainty that the result of invoking that function is undefined
.
void
is not an alias for undefined
and an expression of type void
may have any value, not just undefined
In a function body whose return type is explicitly listed as void
, TypeScript will stop you from "accidently" returning a value, even though this wouldn't create a type system violation. This is helpful for catching bugs that appear from a refactoring:
// Old version
function fn(arr: number[]): void {
const arr1 = arr.map(x => {
return 3;
});
}
// New version
function fn(arr: number[]): void {
for (const x of arr) {
// Oops, meant to do something else
return 3;
};
}