Home > database >  Explicit generator return type in TypeScript demands that `Generator.prototype.return` takes a param
Explicit generator return type in TypeScript demands that `Generator.prototype.return` takes a param

Time:10-17

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.

Playground example

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);
}

Playground Link

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 a try...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"

Playground example

This also solves the type problem, because we're no longer going off the standard path. :-)

  • Related