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
Generators/Async Generators with TReturn type argument which extends undefined have a return method with a non-optional argument in 3.6.2 #33357
Expected behavior:
The argument passed to the return method should be optional. Not sure if this is possible with typescript.
Actual behavior:
file.ts(6,7): error TS2554: Expected 1 arguments, but got 0.
6 gen().return();
~~~~~~~~
typescript/lib/lib.es2015.generator.d.ts:26:12
26 return(value: TReturn): IteratorResult<T, TReturn>;
~~~~~~~~~~~~~~
An argument for 'value' was not provided.
Playground Link:
Playground is not available for 3.6.2
Related Issues: #30790 Strongly typed iterator PR. #32682 Other (async) iterator usability issues. #33239 Trouble implementing async iterator interface in 3.6.2 #33241 Unable to use done property of IteratorResult to narrow types in typescript 3.6.2
changed the title [-]Generators/Async Generators with undefined TReturn argument have a `return` method with a non-optional argument in 3.6.2[/-][+]Generators/Async Generators with TReturn type argument which extends undefined have a return method with a non-optional argument in 3.6.2[/+]on Sep 10, 2019
If a generator indicates it returns number, calling gen.return() without an argument would violate that constraint, as you could force it to return undefined, which is not assignable to number. We currently have no way to make an argument optional based on a type variable that would work in all cases. We could approximate that behavior with conditional types and tuples, but that would cause issues with assignability.
We currently allow you to call next without a value only because the argument you provide the first time you call next on a generator is ignored, but we have no way to restrict subsequent calls to next from eliding a required argument.
Was this intentional? This solution makes sense to me, because the TReturn type describes the return value of the generator type and the void type has additional behavior related to implicit returns.
If the optional-argument behavior of void is intended, I’m okay with this issue being closed as “Working as Intended.” However, the wildly outdated TypeScript spec states:
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.
This feels like a valid use-case for void parameters.
Using void works, but it doesn’t work with the default any, which means that code like the following doesn’t compile:
function*createGen(): Generator<number>{yield1;yield2;}constgen=createGen();console.log(gen.next());// {value: 1, done: false}gen.return();// TReturn is any but return still expects an argument.
If the return type of the generator is any, shouldn’t I be allowed to call return without arguments? This would mimic the way that only functions with a return type of void or any can have an implicit return. This is simply an idiomatic usage of generators which is disallowed by the current typings. I can use the void trick as above, but sometimes I just want to use any because I want to quickly access the IteratorResult value without checking the done property, and void cannot be easily narrowed or eliminated from type unions.
I stand by my comments on the original PR that TReturn should necessarily extend undefined precisely because of situations like this. You can’t use a non-null assertion operator (!) to eliminate void from a type union, so I’m stuck calling gen.return(undefined) when TReturn is any which feels completely wrong.
We could approximate that behavior with conditional types and tuples, but that would cause issues with assignability.
Ron Buckton (@rbuckton)
Why is this not a valid solution? Can you show an example of assignability issues? I feel like a theme with a lot of iterator/generator issues (I am probably the only one who cares 😓) is that you (rbuckton specifically) seem to know a lot about these “issues with assignability” but never give code examples. The example above (calling gen.return without arguments on a generator with a default TReturn) seems like something that should just work.
Activity
[-]Generators/Async Generators with undefined TReturn argument have a `return` method with a non-optional argument in 3.6.2[/-][+]Generators/Async Generators with TReturn type argument which extends undefined have a return method with a non-optional argument in 3.6.2[/+]rbuckton commentedon Sep 18, 2019
If a generator indicates it returns
number, callinggen.return()without an argument would violate that constraint, as you could force it to returnundefined, which is not assignable tonumber. We currently have no way to make an argument optional based on a type variable that would work in all cases. We could approximate that behavior with conditional types and tuples, but that would cause issues with assignability.We currently allow you to call
nextwithout a value only because the argument you provide the first time you callnexton a generator is ignored, but we have no way to restrict subsequent calls tonextfrom eliding a required argument.brainkim commentedon Sep 18, 2019
Did some more playing around. Replacing
undefinedwithvoidin the example above seems to make the argument passed toreturnoptional:Was this intentional? This solution makes sense to me, because the TReturn type describes the return value of the generator type and the
voidtype has additional behavior related to implicit returns.brainkim commentedon Oct 1, 2019
If the optional-argument behavior of
voidis intended, I’m okay with this issue being closed as “Working as Intended.” However, the wildly outdated TypeScript spec states:This feels like a valid use-case for void parameters.
Related issues: #29131
rbuckton commentedon Oct 2, 2019
Yes, the behavior of
voidbeing treated as an optional argument at the end of an argument list is intended and is a recent addition.brainkim commentedon Oct 6, 2019
Using
voidworks, but it doesn’t work with the defaultany, which means that code like the following doesn’t compile:If the return type of the generator is
any, shouldn’t I be allowed to callreturnwithout arguments? This would mimic the way that only functions with a return type ofvoidoranycan have an implicit return. This is simply an idiomatic usage of generators which is disallowed by the current typings. I can use thevoidtrick as above, but sometimes I just want to useanybecause I want to quickly access the IteratorResult value without checking thedoneproperty, andvoidcannot be easily narrowed or eliminated from type unions.I stand by my comments on the original PR that
TReturnshould necessarily extendundefinedprecisely because of situations like this. You can’t use a non-null assertion operator (!) to eliminatevoidfrom a type union, so I’m stuck callinggen.return(undefined)whenTReturnisanywhich feels completely wrong.brainkim commentedon Oct 6, 2019
Ron Buckton (@rbuckton)
Why is this not a valid solution? Can you show an example of assignability issues? I feel like a theme with a lot of iterator/generator issues (I am probably the only one who cares 😓) is that you (rbuckton specifically) seem to know a lot about these “issues with assignability” but never give code examples. The example above (calling
gen.returnwithout arguments on a generator with a defaultTReturn) seems like something that should just work.brainkim commentedon Oct 13, 2019
Related issue about making undefined parameters optional:
#12400