Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does TypeScript have both `void` and `undefined`?

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?

like image 274
Ryan Cavanaugh Avatar asked Nov 15 '19 22:11

Ryan Cavanaugh


People also ask

Is void the same as undefined TypeScript?

void is not the same as any , nor the same as undefined .

What does () => void mean TypeScript?

Introduction to TypeScript void type The void type denotes the absence of having any type at all. It is a little like the opposite of the any type. Typically, you use the void type as the return type of functions that do not return a value.

Does a void function return undefined?

The void operator evaluates the given expression and then returns undefined .

Is void undefined?

The difference is that some browsers allow you to overwrite the value of undefined . However, void anything always returns real undefined. void is an operator and not a function so you dont have to use void() .


1 Answers

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.

Again, 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;   }; } 
like image 166
Ryan Cavanaugh Avatar answered Nov 09 '22 01:11

Ryan Cavanaugh