Skip to content

[Bug]stricter Generators: T not in scope #32507

Closed

Description

@kgtkr

TypeScript Version: 3.6.0-dev.20190720

Search Terms:
Generators

Code

function* foo(): {
  next<T>(x: T): IteratorResult<void, void>;
} {
  // x: T
  const x = yield;
}

Expected behavior:
It is necessary to discuss how it should work

Actual behavior:
x: T

Related Issues:
#30790 #31639

Activity

  1. kgtkr commented on Jul 22, 2019

    @kgtkr
    Author

    other example

    code

    function* foo(): {
      next<T>(x: T): IteratorResult<T, void>;
    } {
      // x: T
      const x = yield 1;
    }

    Expected behavior

    x: number

    Actual behavior

    x: T

    main.ts:5:19 - error TS2322: Type '1' is not assignable to type 'T'.
      '1' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
    
    5   const x = yield 1;
                        ~
    
    
    Found 1 error.
    
  2. changed the title [-]stricter Generators: `T` not in scope[/-] [+]Bug:stricter Generators: `T` not in scope[/+] on Jul 23, 2019
  3. changed the title [-]Bug:stricter Generators: `T` not in scope[/-] [+][Bug]stricter Generators: `T` not in scope[/+] on Jul 24, 2019
  4. falsandtru commented on Jul 25, 2019

    @falsandtru
    Contributor
  5. rbuckton commented on Aug 3, 2019

    @rbuckton
    Contributor

    Your second example looks like it should be an error. If your generator return type says the definition for next is <T>(x: T) => IteratorResult<T, void>, then you should only be yielding the thing that was sent with next, however that's not how generators work.

  6. rbuckton commented on Aug 3, 2019

    @rbuckton
    Contributor

    As odd as it looks, I'm not even certain that the first example is really an error. We contextually type the result of yield as the type of the argument that is passed to next. The only other thing we could do here is possibly make the result unknown or any, but T is actually better here (especially if T had a constraint):

    function * foo(): {
      next<T extends string>(value: T): IteratorResult<unknown, void>;
    } { 
      // x is 'T extends string'
      const x = yield;
      x.charAt(0); // ok
    }

    Daniel Rosenwasser (@DanielRosenwasser), Ryan Cavanaugh (@RyanCavanaugh) this seems like something that might warrant further discussion?

  7. falsandtru commented on Aug 6, 2019

    @falsandtru
    Contributor

    Anyway parametric iterator methods are useless without supporting Rank-2 types.

    function* foo(): {
      next<T>(x: T): IteratorResult<T, void>;
    } {
      // T is `undefined | number`.
      yield undefined; // Return type is `undefined | number`, not `undefined`.
      yield 1; // Return type is `undefined | number`, not `number`.
    }

    https://prime.haskell.org/wiki/Rank2Types

    Overloading may help us but it also brings some inconveniences.

    function* foo(): {
      next<T extends undefined>(x: T): IteratorResult<T, void>;
      next<T extends number>(x: T): IteratorResult<T, void>;
    } {
      yield undefined; // Return type is `undefined`.
      yield 1; // Return type is `number`.
    }
  8. added
    Design LimitationConstraints of the existing architecture prevent this from being fixed
    Working as IntendedThe behavior described is the intended behavior; this is not a bug
    on Aug 9, 2019
  9. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptDesign LimitationConstraints of the existing architecture prevent this from being fixedWorking as IntendedThe behavior described is the intended behavior; this is not a bug

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions