-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Feature request: typed yield expression #36967
Comments
To clarify intent: Currently without an explicit return type annotation all yield expressions give type For context, a lot of the discussion on #30790 is requesting a feature like this. The conversation is well summed up as this:
Higher order generics kind of went out the window being replaced with the much more useful Type inference in conditional types #21496. This suggestion composes well with all already existing constructs, it's just a way to let |
Does this extend to multiple relationships between yielded types and the type of the yield expression? Can you do:
You'd need something like that to make redux saga type-check. I think this proposal seems potentially workable. I'm currently steering away from generators in TS purely because of the lack of yield type-checking, but they are much more powerful than async/await. |
For others that might have landed on this issue trying to find a solution. MobX State Tree uses generator functions for its async actions structure. I have written a library that helps with the typing by turning the flow into a continuation style syntax. https://github.com/mikecann/mst-flow-pipe#readme It means that each step (yeild) in the function is type safe once more. Not a perfect solution but it works for now. |
This is required as TypeScript currently doesn't support implicit casting of yield return values: microsoft/TypeScript#36967
For people who are using something like |
I think this suggestion would be extremely useful and powerful. @tomwidmer, with the suggested implementation overloads are already possible! Check out this code: /** For demonstration purposes */
const uniqueSymbol = Symbol();
type CallExpression<A extends any[], R> = (...args: A) => R;
/** Yield type annotation */
type Yield = <T>(p: T) =>
T extends Promise<infer U> ? U :
T extends CallExpression<any, infer U> ? U :
T extends (infer U)[] ? U :
T extends typeof uniqueSymbol ? 42 :
unknown;
/** Mock yield expression */
const y: Yield = (() => {}) as any;
/** Type checks */
let a = y(Promise.resolve(42)); //number
let b = y(() => 42); //number
let c = y([42]); //number
let d = y(uniqueSymbol); //42
let e = y(42); //unknown This feature would be extremely useful for me personally if implemented. I want to use it in my own code (not with an external library) to write cancelable async workflows. Unfortunately |
Apologies if I'm missing something basic, but would something like this be possible to infer from TypeScript? Edit: Improved the code in #32523 (comment) |
@nightlyherb I think the only 'basic' thing you are missing is that in general the number of yield sites is not always known: interface A {a:string}
interface B {b:string}
function* example(cond1:boolean, cond2:boolean){
if(cond1){
const x: A = yield 1
}
if(cond2){
const y: B = yield 2
}
return "hi there"
} in this case you very much cannot possibly make a What you have written does accomplish exactly what you are describing, the reason it hasn't attracted more attention is that it doesn't serve the general purpose case. |
Search Terms
generator, iterator, yield, type inference, co, redux-saga
Related Issues
#32523
Suggestion
Allow passing of yield operator signature to generator functions
where
yield
inside params list is a pseudo-parameter similar tothis
.Use Cases
Discussion of use cases is given in #32523.
My intended use is case is emulating do notation for monads with generator functions.
This implies I would normally have some generator runner that does composition defined before writing generator function, so type of expression at which generator resumes is trivially inferrable based on yielded value.
Discussion
The hard thing in typing
yield
expressions in is that resumed type does not depend only on passed parameters and generator type, but also on context in which the generator is run.There is a precedent in TypeScript with similar situation --
this
parameter in functions.The situation is analogous -- what
this
behaves like depends on which object function is member of.The proposal is to allow declaring contextual info about
yield
inside generator functions, by introducing appropriate pseudo-param.I could see this working in two ways:
yield
param gets erased early, it does not affect generator function signature,it serves merely as a contract for type-checking / inferrence inside body
yield
param is preserved as type info likethis
param.then it is always present on types of shape
(yield: ...) => Generator<>
and defaults to(a: any) => any
.Additionaly
yield
must extend(a: any) => any
and defaults to ityield
signatureGenerator<>
return type, whether declared orother than obvious coherence guaranteed by (2)
Checklist
My suggestion meets these guidelines:
thats an opt-in feature
type level feature, doesn't affect runtime behavior
The text was updated successfully, but these errors were encountered: