-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve type checking and inference for Generators and Async Generators #30790
Conversation
@typescript-bot test this |
It's great that yield and return types are now treated separately. However it looks like the typing for yield/next types won't be able to model uses like If there are still |
Unfortunately, casts would be necessary. The problems is that you would need a way to relate the yielded type to the next type: function* f(): /* some type to relate 'y' to 'x' */ {
let x = yield y;
} Our compiler does not currently have a mechanism to handle this scenario. |
Well a type-level function from the yielded type to the next type would be simple enough to express in TypeScript. It's just a generic type. For type Next<TYielded> =
TYielded extends Promise<infer U> ? U :
TYielded extends Array<Promise<infer U>> ? U[] :
TYielded; But the type function itself would need be to be passed as a type parameter to |
Just curious, will this proposal support emulating async/await?
if so, what would the Generator type look like? Basically I'm just wondering if properly typing flows from mobx (https://mobx.js.org/best/actions.html - flow section) would be possible after this is in. |
@xaviergonz: That is essentially what @yortus was discussing earlier. No, that is not supported at this time. |
@yortus: Yes, we would probably need some mechanism for higher-order generics which would allow us to do something like this: interface InteractiveGenerator<TNext<TYield>, TReturn> {
next(value?: TNext): IteratorResult<TYield, TReturn>;
return(value: TReturn): IteratorResult<TYield, TReturn>;
throw(err: any): IteratorResult<TYield, TReturn>;
}
type Await<T> = T extends PromiseLike<infer U> ? U : T;
function* f(): InteractiveGenerator<Await, void> {
const x = yield Promise.resolve(1);
// TYield: 'Promise<number>'
// TNext: 'Await<TYield>' -> 'Await<Promise<number>>' -> 'number'
// x: 'number'
} If we do add a feature for higher-order generics in the future, I imagine this would be feasible to implement over a separate interface like |
0e23eac
to
8204898
Compare
@typescript-bot test this |
I just remembered #1213, which tracks higher-order generics. So if that issue was resolved, then usecases like |
Looks like the RWC errors fall into two categories:
|
@typescript-bot run dt |
DT Failures fall into one of three categories:
The tests for 'adone' and 'ckeditor' can be easily fixed though I need to take a closer look at the 'co' tests. The errors in Node were expected, and we've already addressed them using |
redux-saga is a very popular library that uses generators in a way similar to co and which would benefit from what you're describing. |
This will be a great addition!
|
I assume
Correct. This is because the (async) generator only starts executing after the first There's no way to encode this behavior at the type level. To know whether
|
We already addressed this issue in |
@rbuckton Awesome! Thank you so much — That fixed it! You just saved me hours of research. ❤ |
From what I have understand here - are you saying that improving types around co-like libraries is not on the schedule of 3.6? I was kinda hoping that advertised "improved generators support" was all over that. I might be biased (redux-saga maintainer here) but implementing all sort of interpreters with generators is a really cool usage and was hoping that upcoming changes would finally help express those patterns in TS. |
@Andarist It sounds like your use case is the same as what @yortus mentioned above. If so, you're correct in that this PR does not address that. Such a use case requires type-level higher-order functions, the implementation of which would be a much more fundamental change to the type system, and is very much beyond the scope of this work. Unfortunately, there do not seem to be any plans (that I can find) to add such a feature to the language. :( |
This appears to be the root cause of palantir/tslint#4784. |
This PR changes the definitions for
IteratorResult
,Generator
, andAsyncGenerator
and makes a number of improvements in how we check and infer types foryield
andreturn
inside of the body of a generator or async generator.Type Definition Changes:
Type Checker Changes
With these new definitions, a generator can now distinguish between the following three types:
yield
(i.e.TYield
above)return
(i.e.TReturn
above)next()
on the generator (i.e.TNext
above).Inside of the type checker we have also made the following changes to improve checking and inference when inside the body of a generator or async generator:
return
against the returned type of the generator's return type annotation (i.e. theTReturn
type in theGenerator
definition above).yield
expression based on the next type of the generator's return type annotation (i.e. theTNext
type in theGenerator
definition above).yield
expression.function*f(){}
as an implicit-any error, and will instead give it a return type ofGenerator<never, void, unknown>
. This aligns with our behavior forfunction f(){}
where we infer a return type ofvoid
.Here are several examples of the new behavior:
Notes
TReturn
has a default ofany
. This is necessary for backwards compatibility when assigning a{ done: boolean, value: number }
type to anIteratorResult<number>
, which is often the case when the source type is result of widening an object literal's type when there is no contextual type.For existing code using weakly-typed iterators (i.e.{ next(): { done: boolean, value: T } }
) to still be considered asssignable, this PR depends on Relate source types covered by a target discriminated union #30779 (Relate source types covered by a target discriminated union).Fixes #13847 (types will be checked correctly if return type is specified as
Generator<T, void>
)Fixes #26959
Fixes #2983
Fixes #21527
Fixes #31214