question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature request: typed yield expression

See original GitHub issue

Search Terms

generator, iterator, yield, type inference, co, redux-saga

Related Issues

#32523

Suggestion

Allow passing of yield operator signature to generator functions

declare function fetchUser(): Promise<User>;
function asyncAction*(yield: <R>(p: Promise<R>) => R) {
  const user = yield fetchUser(); // user type inferred to User based on yield param
  // impl
}

where yield inside params list is a pseudo-parameter similar to this.

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
function asyncAction*(yield: <R>(p: Promise<R>) => R) {
  // impl
}
typeof asyncAction == () => Generator<...>
  • yield param is preserved as type info like this param. then it is always present on types of shape (yield: ...) => Generator<> and defaults to (a: any) => any.
function asyncAction*(yield: <R>(p: Promise<R>) => R) {
  // impl
}
typeof asyncAction == (yield: <R>(p: Promise<R>) => R) => Generator<...>

Additionaly

  1. signature of yield must extend (a: any) => any and defaults to it
function asyncAction*(yield: number) {
  // impl
}
// Error: signature must be of shape (any => any)
  1. yielded values inside generator function body must be assignable to argument of yield signature
function asyncAction*(yield: (s: string) => void) {
  yield 7; // Error: number is not assignable to string
}
  1. this parameter has no effect on Generator<> return type, whether declared or other than obvious coherence guaranteed by (2)

Checklist

My suggestion meets these guidelines:

  • This wouldn’t be a breaking change in existing TypeScript/JavaScript code thats an opt-in feature
  • This wouldn’t change the runtime behavior of existing JavaScript code type level feature, doesn’t affect runtime behavior
  • 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.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:23
  • Comments:6

github_iconTop GitHub Comments

1reaction
tadhgmistercommented, Sep 4, 2020

For people who are using something like redux-saga and looking for a work around, I want to make my work around here easier to find (particularly for myself 😄

1reaction
mikecanncommented, May 21, 2020

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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

'yield' expression implicitly results in an 'any' type because its ...
I got same error and I solved it. export interface ResponseGenerator{ config?:any, data?:any, headers?:any, request?:any, status?:number, ...
Read more >
yield - JavaScript - MDN Web Docs - Mozilla
The yield operator is used to pause and resume a generator function.
Read more >
Error: 'yield' expression implicitly results in an 'any' type ...
TypeScript 4.2 not only adds a number of features, but also includes changes that may affect your application when it uses generators. If...
Read more >
Documentation - TypeScript 3.6
If you'd prefer to be explicit, you can also enforce the type of values that can be returned, yielded, and evaluated from yield...
Read more >
How to Use Generators and yield in Python
Building Generators With Generator Expressions; Profiling Generator ... the functionality of generators rests, so let's dive into how yield works in Python.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found