You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Types for async functions do not handle void assignments like normal ones #33420
Expected behavior:
Generally speaking, all of them should be an error, or all of them shouldn't be.
Actual behavior:
We have a special "function returning void" rule that allows () => "" to be assignable to () => void, but this doesn't carry thru to async scenarios, so a async () => "" is not assignable to a () => Promise<void>.
There are two rules we have in place governing the above:
void-returning functions should not return non-undefined values.
A function type whose return value is voidis treated as though thatvoidwereunknown`, for the purposes of assignability.
This creates this annoying inconsistency where even though function statement/expressions with annotated returns of void are checked for undefined-ness, consts with function type annotations to which function expressions are assigned are not checked in the same way. Separately, the second rule fails to affect async functions in situations where you'd expect it should, since they don't return void verbatim, but instead return Promise<void>.
This has caused me pain already when writing typings for miniSphere. Generally speaking I like the rule where a void return acts as any for the purpose of assignment, as it’s useful in HOF scenarios (callbacks e.g.), and it frustrates me that I can’t get the same behavior with async functions.
This does seem a bit tricky though: if we made the void assignment rule work for Promise<void> would it then end up affecting any generic with a void type argument? I’m not sure whether that’s desirable...
And then even if it is... what about when the type parameter is contravariant? Or invariant? Does variance affect it at all? It’s a pretty big can of worms.
Tbh, I think void just needs to behave like unknown except we also have lint-level checks to attempt to forbid real-valued returns in implementations whose expected return position type is void.
Activity
weswigham commentedon Sep 13, 2019
There are two rules we have in place governing the above:
void-returning functions should not return non-undefinedvalues.A function type whose return value isvoidis treated as though thatvoidwereunknown`, for the purposes of assignability.This creates this annoying inconsistency where even though function statement/expressions with annotated returns of
voidare checked forundefined-ness,consts with function type annotations to which function expressions are assigned are not checked in the same way. Separately, the second rule fails to affect async functions in situations where you'd expect it should, since they don't returnvoidverbatim, but instead returnPromise<void>.fatcerberus commentedon Sep 13, 2019
This has caused me pain already when writing typings for miniSphere. Generally speaking I like the rule where a
voidreturn acts asanyfor the purpose of assignment, as it’s useful in HOF scenarios (callbacks e.g.), and it frustrates me that I can’t get the same behavior with async functions.fatcerberus commentedon Sep 14, 2019
This does seem a bit tricky though: if we made the
voidassignment rule work forPromise<void>would it then end up affecting any generic with avoidtype argument? I’m not sure whether that’s desirable...And then even if it is... what about when the type parameter is contravariant? Or invariant? Does variance affect it at all? It’s a pretty big can of worms.
weswigham commentedon Sep 14, 2019
Tbh, I think
voidjust needs to behave likeunknownexcept we also have lint-level checks to attempt to forbid real-valued returns in implementations whose expected return position type isvoid.fatcerberus commentedon Sep 14, 2019
I don’t know, I think it’s kind of elegant right now that
voidis in a duality withany. 😃any: Do whatever you want, I won’t bother you but runtime errors are your problem nowvoid: I’ll put whatever here, don’t try to stop me (by inspecting the value) or elseWhere “you” = programmer, “I” = type system.
Dan503 commentedon Oct 19, 2022
I've noticed that void doesn't behave as expected for the following circumstance either:
This should throw an error because
() => () => void!=() => voidThis bug caused me to spend a multiple hours trying to figure out what I was doing wrong.
It's not easy spotting that
() => () => {}should be() => {}especially when you are expecting TS to detect type conflict issues like this.RyanCavanaugh commentedon Jul 25, 2025
I think this is effectively #42709, let's track it there