I have a generator function acting as a consumer to accumulate strings:
function *concatStrings(): Generator<void, string, string> {
let result = '';
try {
while (true) {
const data = yield;
result = data;
}
} finally {
return result;
}
}
const g = concatStrings();
g.next();
g.next('abc');
const r = g.return();
console.log(r);
This works fine, but in TS, it complains
Expected 1 arguments, but got 0.ts(2554)
lib.es2015.generator.d.ts(26, 12): An argument for 'value' was not provided.
How do I create a version of the top that TS doesn't complain, but I also end up with the right types?
CodePudding user response:
The return
method is defined as requiring a parameter. If a parameter type is void
it does not have to be passed in. We can specify the return type as string | void
. This will allow us to not specify the return type:
function *concatStrings(): Generator<void, string | void, string> {
let result = '';
try {
while (true) {
console.log("Loop")
const data = yield;
result = data;
}
} finally {
console.log("Cleanup")
return result;
}
}
const g = concatStrings();
g.next();
g.next('abc');
const r = g.return();
if(typeof r.value === "string") {
console.log(r.value);
}
This will mean that r.value
will be void | string, which is less than ideal
so you will need to check for string
before you can use it.
Another option is to pass in null!
as an argument to return g.return(null!);
(Playground Link)
CodePudding user response:
The problem is that you're overriding the usual mechanism of the return
method by using a return
statement in a finally
block that throws away the value provided to the method and returns result
instead. That's why the signature doesn't match up. In the normal case, the parameter value of the return
method is also the TReturn
of the generator (example). From MDN:
Generator.prototype.return()
Acts as if a
return
statement is inserted in the generator's body at the current suspended position, which finishes the generator and allows the generator to perform any cleanup tasks when combined with atry
...finally
block.
(Cleanup tasks don't usually include a return
statement.)
For what it's worth, I'd encourage you to define and use your generator slightly differently — just use the result of next
, no need for that final return
, like this:
function *concatStrings(): Generator<string, void, string> {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^
let result = '';
// (Note no `try`/`finally`, since you don't have resources to clean up)
while (true) {
const data = yield result;
// −−−−−−−−−−−−−−−−−−−−−−−−^^^^^^
result = data;
}
}
const g = concatStrings();
let r: IteratorResult<string, void>;
g.next();
r = g.next("a");
console.log(r.value); // "a"
r = g.next("b");
console.log(r.value); // "ab"
r = g.next("c");
console.log(r.value); // "abc"
This also solves the type problem, because we're no longer going off the standard path. :-)