Typescript: Unexpected type for Generator

Created on 15 Jan 2020  路  4Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.7.2


Search Terms:

Expected behavior:

let barGenerator: Generator<any, string, { name: string; } | { age: number; }>;

Actual behavior:

let barGenerator: Generator<any, string, { name: string; } & { age: number; }>;


Related Issues:

Code

function* Bar() {
  let x: { name: string } = yield;
  let y: { age: number } = yield;

  return x.name + y.age;
}

let barGenerator = Bar();
// let barGenerator: Generator<any, string, { name: string; } & { age: number; }>;

let barResult = barGenerator.next();

barResult = barGenerator.next({ name: "Bar" }); // Error: Argument of type '[{ name: string; }]' is not assignable to parameter of type '[] | [{ name: string; } & { age: number; }]'.
barResult = barGenerator.next({ age: 40 });

console.log(barResult);

Output

"use strict";
function* Bar() {
    let x = yield;
    let y = yield;
    return x.name + y.age;
}
let barGenerator = Bar();
//let barGenerator: Generator<any, string, { name: string; } & { age: number; }>
let barResult = barGenerator.next();
// Error: Argument of type '[{ name: string; }]' is not assignable to parameter of type '[] | [{ name: string; } & { age: number; }]'.
barResult = barGenerator.next({ name: "Bar" });
barResult = barGenerator.next({ age: 40 });
console.log(barResult);

Compiler Options

{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided

Question

Most helpful comment

IMHO statically typing yield is impossible as soon as you start using different input types because TS can't enforce types order in general case.

In this case you can't rely on inferred type and need to add explicit annotation. It can be Generator<undefined, string, any> or more involved Generator<undefined, string, string | number> and type assertions

But this will be your responsibility to math expected and passed types at runtime.

All 4 comments

Only type that has both name and age can be assigned to both x and y.
See example
Only intersection satisfy both constraints but union would lead to a problem

let barResult = barGenerator.next();

barResult = barGenerator.next({ name: "Bar" });
barResult = barGenerator.next({ name: "Bar" }); // allowed by union, but not assignable to { age: number }

Only type that has both name and age can be assigned to both x and y.
See example
Only intersection satisfy both constraints but union would lead to a problem

let barResult = barGenerator.next();

barResult = barGenerator.next({ name: "Bar" });
barResult = barGenerator.next({ name: "Bar" }); // allowed by union, but not assignable to { age: number }

If x has type string and y has type number, then intersection will be never. And so, I can't use the generator at all.

function* Foo() {
  let x: string = yield;
  let y: number = yield;

  return x + y;
}

let fooGenerator = Foo();
// let fooGenerator: Generator<undefined, string, never>

let fooResult = fooGenerator.next();

fooResult = fooGenerator.next("Foo"); // Error: Argument of type '["Foo"]' is not assignable to parameter of type '[] | [never]'
fooResult = fooGenerator.next(40); // Error: Argument of type '[40]' is not assignable to parameter of type '[] | [never]'

console.log(fooResult);

IMHO statically typing yield is impossible as soon as you start using different input types because TS can't enforce types order in general case.

In this case you can't rely on inferred type and need to add explicit annotation. It can be Generator<undefined, string, any> or more involved Generator<undefined, string, string | number> and type assertions

But this will be your responsibility to math expected and passed types at runtime.

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

weswigham picture weswigham  路  3Comments

dlaberge picture dlaberge  路  3Comments

jbondc picture jbondc  路  3Comments

seanzer picture seanzer  路  3Comments

kyasbal-1994 picture kyasbal-1994  路  3Comments