Repository navigation
Open
Labels
In DiscussionNot yet reached consensusNot yet reached consensusSuggestionAn idea for TypeScriptAn idea for TypeScript
Description
Search Terms
non returning, undefined instead of void, undefined or unknown instead of void
Suggestion
- Change return of non returning functions to
undefinedinstead ofvoid - Accept
unknownas a type of non returning functions
examples:
const f = () => {} // this gets inferred as () => void
const f1 = (): undefined => {} // doesn't work
// error: A function whose declared type is neither 'void' nor 'any' must return a value.(2355)
const f2 = (): unknown => {} // same errorMotivations
void is an annoying type to deal with, has many inconsistencies and there's a ton of issues around it for its weird behavior like: #35850 , #35236 , #33420 ... etc
Non-returning functions actually return undefined which is a more predictable type to deal with and narrow disjunctions with and even use optional chaining with.
Use Cases
Many use cases appear while using promises where there's a need to catch an error but at the same time not deal with void
const f = () => {}
async (p: Promise<A>) => {
const a = await p.catch(f);
// do stuff with a whose type is now A | void
};Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
Not sure if this might break some code or not but I think any code that dealt with void should work while dealing with undefined?
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Reacted by sharno, William Furr, Alexandre Galays, Brian Kim, Rajas Paranjpe, Wojciech Pawlik, VladimirK, Sofie Hanzlíková, Areum Yang, Remco Haszing and 5 more
Reacted by Brian Kim
Reacted by Andrii Dieiev
Reacted by Brian Kim, Duncan Walter, Remco Haszing and lionel-rowe
Reacted by Brian Kim
Reacted by Brian Kim and lionel-rowe
Metadata
Metadata
Assignees
Labels
In DiscussionNot yet reached consensusNot yet reached consensusSuggestionAn idea for TypeScriptAn idea for TypeScript
Activity
nmain100 commentedon Jan 17, 2020
I like the current behavior. If I write a function with no
return <expr>in it at any point, then there's no reason for me to try to observe the return value of it, and any attempt to observe the return value is probably a mistake. I like it when Typescript catches my mistakes.sharno commentedon Jan 17, 2020
undefinedis a more specialized and narrower type thatvoidMy guess is if a function always returns
undefinedthere's no reason to observe its return type either.Another case is if you are using a function of type
() => voidin thecatchpart of a promise, you'll end up with a type like this:Promise<A | void>, at this point you care to know that your catch isn't actually returningAwhich will be hard to handlefalsandtru commentedon Jan 17, 2020
I hate the following error. The compiler requires a return statement although I just intend to reject null.
RyanCavanaugh commentedon Jan 17, 2020
Let's say today you write a function and there's no useful value to return.
Then, next month, you decide that it'd be better if the function returned a number of some kind.
Is this a breaking change in your API contract?
If the non-returning version of the function was marked as
void, then no, it can't be a breaking change, because the rules aroundvoideffectively prevent a consumer from depending on the return value.If the non-returning version of the function was marked as
undefined, then yes, it absolutely can be a breaking change. Code like thiswould have been an error under
void, but is OK under: undefinedand now changes behavior. There is substantial risk of downstream code changing behavior when a function goes from returning "nothing" to returning "something"; having the "nothing" case bevoidmitigates that risk.What are we getting in return? The "confusion" around
voidis generally around people not understanding what it means to mark a function's return value as unspecified, which is its primary source of future-safety.sharno commentedon Jan 17, 2020
I disagree, for me, specified types/behavior trumps unspecified types/behavior. and I think that contradicts with your comments before that specified behavior should be annotated as so. Non-returning functions have a specified behavior too.
#35236 (comment)
Anyway, if you plan not to accept this issue, is it possible to accept an new issue to allow this?
RyanCavanaugh commentedon Jan 17, 2020
I think those are pretty reasonable, at least with
noImplicitReturnsofffalsandtru commentedon Jan 18, 2020
Ryan Cavanaugh (@RyanCavanaugh) #36239 (comment) is not the issue here. I mean the compiler can know that the return type can be undefined from the function body. This is an issue of type inferences rather than type checking.
falsandtru commentedon Jan 18, 2020
If the non-returning version of the function was marked as
undefinedby the type of the receiver, probably it can't be a breaking change.falsandtru commentedon Jan 18, 2020
So I disagree with author's use case but the compiler should allow non-returning functions to return the
undefinedtype in some cases.falsandtru commentedon Jan 18, 2020
I separated the issues into #36288 because declaring
undefinedreturn type with no context is not my main intent here.ssalka commentedon Oct 12, 2020
EDIT: it looks like #36288 might be a better place for me to raise the below case? Not really sure, as both issues seem relevant.
I'm running into a situation where I have a
func(): Promise<void | Result>which can possibly return a value, but not necessarily so.At the call site, I'm trying to do:
however the compiler flags this an error, as optional chaining doesn't work on
voidtypes (assuming this is intended behavior?). Oddly, it does work if I instead do:This seems weird to me, since I expect
res?.fieldto be equivalent to / interchangeable withres && res.field.The only thing I can think to do, besides using the
res && res.fieldcondition, is changing the function to instead returnPromise<undefined | Result>, however I dislike this approach because it requires that the function explicitly return undefined, which I consider to be unnecessary.AlexGalays commentedon Nov 4, 2020
This would be very useful.
Imagine a function a bit like found in immer where the return type is void, and an object is passed for the sole purpose of being mutated (it's actually a proxy that doesn't get mutated but that's how the api looks like from the outside)
so you expect people to write something like:
If one doesn't pay attention, it's very easy to return a transformed object and expect this change to be picked up. It won't do anything and there is no compilation error:
This is not a fiction by the way, this happened 5 minutes ago 😆
The type system could really help us detect these usage mistakes.
EDIT: Nevermind, it seems
() => void | undefinedalready does exactly that. Is there any drawback?ChocolateLoverRaj commentedon Jan 24, 2021
sharno (@sharno) I agree
I think the
voidtype should be removed from typescript.wojpawlik commentedon Feb 10, 2021
Ryan Cavanaugh (@RyanCavanaugh), perhaps TypeScript should complain about this code? typescript-eslint already does:
undefinedis a pretty useless type already, I only found one more way to misuse it:It's possible to similarly misuse
void: