I would like to send a value back to the generator function using next
which seems to complicate typing this function
function* gen(): IterableIterator<string> { const a = yield 'test'; // Line A console.log('received:' + a); yield a + ''; } const x = gen(); console.log('main:' + x.next(99)); // Line B console.log('main' + x.next());
In my VSCODE I get the following error for Line A
Type 'undefined' is not assignable to type 'number'.
And In Stackblitz/demo I get an error for Line B
Argument of type '[99]' is not assignable to parameter of type '[] | [undefined]'. Type '[99]' is not assignable to type '[undefined]'. Type '99' is not assignable to type 'undefined'.(2345)
So my question is, how can I type the value I provide with next
?
Advertisement
Answer
TypeScript 3.6 introduced support for stricter generator typing, including the Generator<Y, R, N>
type where the Y
type parameter corresponds to the type yield
ed from the generator function body (the same as the T
in Iterator<T>
), the R
type parameter corresponds to the type return
ed from the generator function body, and the N
type parameter corresponds to the type passed into the next()
method of the iterator. Since you are passing string
to yield
and passing number
to next
and not return
ing anything, it looks like you want your generator return type to be something like Generator<string, void, number>
:
function* gen(): Generator<string, void, number> { const a: number = yield 'test'; console.log('received: ' + a); yield a + ''; } const x = gen(); const y = x.next(99); if (y.done) throw new Error(); console.log("main: " + y.value) // main: test const z = x.next(); // received: undefined if (z.done) throw new Error(); console.log("main: " + z.value) // main: undefined
It is a little weird that a
is typed as number
but could be undefined
, even with the --strictNullChecks
compiler option enabled. But that’s what happens if you call x.next()
without an input. This is apparently working as intended as per this comment on ms/TS#30790, the implementing pull request. So if you ever plan to do something that would explode if undefined
comes out of that yield
, like this:
function* gen(): Generator<string, void, number> { const a: number = yield 'test'; console.log('received:' + a.toFixed(2)); // ERROR if a is undefined yield a.toFixed(2); }
then you should probably manually augment the N
type parameter with undefined
to be safe:
function* gen(): Generator<string, void, number | undefined> { // -------------------------------------------> ^^^^^^^^^^^ const a = yield 'test'; console.log('received: ' + (a?.toFixed(2))); yield a?.toFixed(2) || "undefined"; } const x = gen(); const y = x.next(99); if (y.done) throw new Error(); console.log("main: " + y.value) // main: test const z = x.next(); // received: undefined if (z.done) throw new Error(); console.log("main: " + z.value) // main: undefined