TypeScript Version: 2.9.2
Search Terms: void undefined
Code
function a(b: (c: undefined) => undefined) { }
function b(c: void) { }
a(b)
Expected behavior: As void is undefined I don't see why this should error.
Actual behavior: Unnecessary error that forces the use of void instead of undefined if you interface with a library that is typed with void instead of undefined.
Playground Link: http://www.typescriptlang.org/play/#src=function%20a(b%3A%20(c%3A%20undefined)%20%3D%3E%20undefined)%20%7B%20%7D%0Afunction%20b(c%3A%20void)%20%7B%20%7D%0A%0Aa(b)
void is not undefined. void means the absence of a return value. undefined is the type of the value undefined at runtime.
it is true that a function that returns no value at run time returns undefined, but in the TS type system we chose to make the absence of a return value special. For example assigning (a) => void to (a) => number | undefined is likely an error, though it is safe at run-time.
In general do not use void except in the return type of functions. for everything else, use undefined.
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
What about this use case:
function f(): void | string {}
function g(x?: string): void {}
g(f());
In general do not use
voidexcept in the return type of functions. for everything else, useundefined.
In order to get this to work, I either have to to change void to undefined in the return type of f, which kinda contradicts
In general do not use
voidexcept in the return type of functions
or, I union the parameters with void: function g(x: void | string), which contradicts
for everything else, use
undefined
and also means I lose the handy (and descriptive) optional parameter syntax
To further my previous comment, I have a getter function that looks for a value but returns nothing if the value isn't found, hence why I think it's appropriate to return void in accordance with
voidmeans the absence of a return value
I then have a function that processes the input but returns an "empty" object if there is no input. This means I can pipe the output of the getter into the processor function without doing any extra checks. The processor does not necessarily receive input from the getter, hence why I think it's appropriate to use an optional parameter, as the processor can successfully be called without arguments to get an empty class.
This leads to the situation above:
// `getter` *might* return a value, hence `void`
function getter(): void | string {}
// `processor` can be called without arguments, hence an optional parameter
processor(x?: string): string {}
// `getter` should feed nicely into `processor`:
processor(getter()) // error - void is not assignable to string | void
Hopefully that shows my use case is justified and not contrived
Anecdotally, void's distinction from undefined is one of my biggest pain points when working with TypeScript. It's also very strange, given that the entire reason JavaScript distinguishes undefined from null is for this exact type of case: the absence of a value instead of an explicitly null value. I really don't understand the benefit of adding yet another tier of distinction that's specific to functions.
At the very least, a compiler option that makes void equivalent to undefined (instead of the much more extreme strictNullChecks=false) would be much-appreciated.
The behavior here is 100.000% intentional, and the absence of such a flag is 100.00000% intentional. If you think you want that flag, you have not really understood why void exists.
Can you enlighten me, then?
Using
voidinstead means thatforEachpromises not to use the return value
The line above from that StackOverflow answer is what made things click for me.
After posting my comment above I had a second look at my code, turns out I actually returning undefined anyway:
function f(): void | string {
...
return data[id];
}
There is, of course, still the contrived case where the function still can return a value or not return at all:
function f(): void | string {
if(id) {
return data[id];
}
}
But in this case the return type should probably still be given as undefined | string.
Perhaps there should be (if there isn't already) a @typescript-eslint rule here telling me to change void to undefined?
I guess I'm late to this, and I don't know if anyone still reads this. But maybe it helps someone. It seems to me that void means two possible things depending on parsing context:
void is close to any, and is useful to signify that the return value will not be used. Thus it's a way for functions taking callbacks to indicate that any type is accepted as a return.undefined). This is also true for variable declarations, and maybe other contexts. If used in a union type, the other types are allowed. Thus the following happens, which can seem surprising
type VoidFunc = () => void
const a:VoidFunc = () => 'bar'; // ok
const b:void = 'bar'; // TS error
function c(): void {} // ok
function d(): void { return 'bar'; } // TS error
function e(): void { return undefined; } // ok
function f(): void | string { return 'bar'; } // ok
function g(cb:() => undefined){}; g(() => {}) // TS error
At least, g above is surprising to me. @RyanCavanaugh - any way that can be dealt with better?
Also, why not just disallow void outside of function return types?
In general we don't treat an implicit function exit as the same as return undefined;; some people like this and some don't and I can see both sides of that. If the caller is really expecting you to return the exact value undefined, first, why?, and second, it's probably best to write it explicitly for the sake of any future maintainer who thinks it's OK to return some other value (e.g. you start returning a success code or something, which is typically not treated as a break in the function contract).
Thanks for the reply @RyanCavanaugh . I can see that. I think the reason is when you have contracts and want to say: It's ok to not return anything (undefined), but _if_ you do, it has to be type Foo. And if those functions are expected to be short style lambdas, making the requirement of an extra return undefined is a little cumbersome.
Most helpful comment
voidis notundefined.voidmeans the absence of a return value.undefinedis the type of the valueundefinedat runtime.it is true that a function that returns no value at run time returns
undefined, but in the TS type system we chose to make the absence of a return value special. For example assigning(a) => voidto(a) => number | undefinedis likely an error, though it is safe at run-time.In general do not use
voidexcept in the return type of functions. for everything else, useundefined.