Home > Enterprise >  How to specify type of an infinite generator?
How to specify type of an infinite generator?

Time:05-09

Here is a sample JavaScript code:

/**
 * Generates the sequence of numbers.
 * 
 * @param {number} i - The first number.
 * @yields {number} The next number.
 */
function* gen(i) {
  while (true) {
    yield i  ;
  }
}

const g = gen(1);

// 1st case
// No error here
const n = g.next();
if (!n.done) {
  const x = n.value * 2;
  console.log(x);
}

// 2nd case
// Error:
//   The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
// Other variants of the error for other expressions:
//   Type 'number | void' is not assignable to type 'number'.
//   Type 'void' is not assignable to type 'number'
const y = g.next().value * 2;
console.log(y)

gen function generates an infinte sequence of numbers. So I don't need to check whether it's finished.

Is it possible to remove the typecheck error in the 2nd case? Here is a similar question: How to avoid void type in generators Typescript. The following suggestions was given:

  1. check first if done is true and act accordingly (early return, throw, whatever you need);
  2. if you know that the iterator will always return a value you can use a non-null assertion.

But I don't want to check done. And I can't add a non-null assertion, because it's JavaScript, not TypeScript. Could you suggest how to remove the error?

Here is jsconfig.json:

{
  "compilerOptions": {
    "lib": ["es2021"],
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,
    "module": "es2022",
    "target": "es2021",
    "strict": true,
    "strictPropertyInitialization": false,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "allowUnusedLabels": false,
    "allowUnreachableCode": false,
    "exactOptionalPropertyTypes": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUncheckedIndexedAccess": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "importsNotUsedAsValues": "error"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

CodePudding user response:

TypeScript is protecting you from g.return(). There does not exist a generator function which can never be done. The Generator.prototype.return() method can even be implicitly called when using generator objects in for...of loops:

function* gen(i) {
  while (true) {
    yield i  ;
  }
}

const g = gen(1);

for (const n of g) {
  if (n > 5) break;
  console.log(n);
}

console.log(g.next());

CodePudding user response:

I don't think this is a problem with Typescript, since it is supposed to catch potential code errors like this.

If you're absolutely certain that the generator will not return a null value, you could explicitly write out the type like so: const y = (g.next().value as number) * 2;

  • Related