Why void functions are allowed in left part of assignment in Typescript?

955 views Asked by At

Consider the following Typescript code:

var action = function action(): void {
    //do things
};

var res = action();

Why is it fine for TS compiler?

3

There are 3 answers

0
Jurijs Kovzels On

Most likely it is because, void is a type in Typescript and it has it's set of possible values, which are undefined and null, therefore can be used in left part of assignments.

I find the fact that it allowed to use values typed as void in assignments could lead to severe errors. For example if one had a function returning something meaningful was refactored to return void, there will be no error at compile time.

For the reference Typescript 1.4 language spec have the following to say about Void type:

The Void type [...] represents the absence of a value and is used as the return type of functions with no return value. The only possible values for the Void type are null and undefined. The Void type is a subtype of the Any type and a supertype of the Null and Undefined types, but otherwise Void is unrelated to all other types.
NOTE: We might consider disallowing declaring variables of type Void as they serve no useful purpose. However, because Void is permitted as a type argument to a generic type or function it is not feasible to disallow Void properties or parameters.

1
basarat On

Why void functions are allowed in left part of assignment in Typescript?

Simply because they are not disallowed. Note that there isn't must useful (bad) stuff you can do with a void variable e.g.

var action = function action(): void {};
var res = action();
res.foo; // Error 
0
Ryan Cavanaugh On

Consider this code:

// Generic function for perf measuring
function time<T>(f: () => T): T {
  var start = Date.now();
  var result = f();
  console.log('It took ' + (Date.now() - start) + ' to run');
  return f;
}

function doSomething(): void { }

time(doSomething);

If we think about generic functions as being type-specific instantiations of a template (note: they aren't), result is of type void when time is invoked with doSomething. Clearly the world doesn't explode when that happens, so having a variable of type void isn't something that must be prevented.

Here's another example:

declare function doNothing(): void;
declare function doOneThing(): void;
declare function doManyThings(): void;

function doSomething(x: number) {
    switch (x) {
        case 0:
            return doNothing();
        case 1:
            return doOneThing();
        default:
            return doManyThings();
    }
}

Here, we want to make it so that if we change the return type of doSomething, we have to update the return types of doNothing/doOneThing/doManyThings. Again, here's the general principle that a void value is moving around with no ill effect. This example is not artificial - it's adapted from some code in the TypeScript compiler itself.

Let's refactor that code a bit:

function doSomething(x: number) {
    if (x > 1) {
        return doManyThings();
    } else {
        // result: void!
        var result = x === 0 ? doNothing() : doOneThing();
        return result;
    }
}

Why should this harmless refactoring introduce an error? What's the point? If we try to access any properties of result, that will be an error. If we don't try to access any properties of result, it's likely we're doing nothing wrong and shouldn't be bothered.