Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking β€œSign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Naked generic type returned from iterator method #59161

Open
reverofevil opened this issue Jul 7, 2024 Β· 5 comments
Open

Naked generic type returned from iterator method #59161

reverofevil opened this issue Jul 7, 2024 Β· 5 comments
Assignees
Labels
Bug A bug in TypeScript

Comments

@reverofevil
Copy link

reverofevil commented Jul 7, 2024 β€’

πŸ”Ž Search Terms

generic generator static

πŸ•— Version & Regression Information

  • I was unable to test this on prior versions because there is still no online version of every-ts bisect

⏯ Playground Link

Link

πŸ’» Code

class Base {
    static *[Symbol.iterator]<T>(this: T) {
        yield this;
    }
}
class Child extends Base {}
const test = function* () { yield* Child };
const value = [...test()][0]; // T
console.log(value); // Child

πŸ™ Actual behavior

The result is typed as T, whatever generic parameter outside of generic code means.

πŸ™‚ Expected behavior

T should be instantiated with Child during a call to [Symbol.iterator]

Additional information about the issue

For regular static method calls it works as expected, by instantiating T

class Base {
    static foo<T>(this: T) {
        return this;
    }
}
class Child extends Base {}
const value = Child.foo(); // Child
console.log(value); // Child

On the other hand, this is a nice way to create unique types for opaque type implementation! πŸ™ƒ

@reverofevil reverofevil changed the title Naked generic type in this type for generator static method Naked generic type for this type of iterator method Jul 7, 2024
@Andarist
Copy link
Contributor

Andarist commented Jul 7, 2024 β€’

This is a fun one too:

class Base {
  static *[Symbol.iterator]<T extends [1, 2]>() {
    yield {} as T;
  }
}
class Child extends Base {}
const test = function* () {
  yield* Child;
};
const value = [...test()][0];
//    ^? const value: T extends [1, 2]

Basically, all type parameters leak in this situation.

@reverofevil
Copy link
Author

Also it doesn't have to be static. I left it in the example, because otherwise it's harder to see that the behavior is incorrect.

This is all it actually takes:

class Base {
  *[Symbol.iterator]<T>() {
    yield {} as T;
  }
}

const x = [...new Base()][0];
//    ^? const x: T

I tried another Symbol() there, and it works fine.

@Andarist
Copy link
Contributor

Andarist commented Jul 7, 2024 β€’

The proper fix for this is to thread the obtained [Symbol.iterator] signatures through resolveCall. Just like this PR does things with [Symbol.hasInstance]: #55052

I have this working locally but it needs a lot of cleanup (implementation of iteration protocol has many layers/helper functions) and investigating all of the other ways of iterating through things. The problem is not exclusive to spreads:

class Base {
  *[Symbol.iterator]<T>() {
    yield {} as T;
  }
}

for (const element of new Base()) {
  element;
  // ^? const element: T
}

@reverofevil reverofevil changed the title Naked generic type for this type of iterator method Naked generic type returned from iterator method Jul 8, 2024
@reverofevil
Copy link
Author

reverofevil commented Jul 8, 2024 β€’

Oof! It actually doesn't do resolveCall stuff at all! Not only this shouldn't compile (foo comes from where?), but if you leave only one signature, it infers correct type.

declare class Base {
  [Symbol.iterator](foo: 1): Generator<3, void, undefined>;
  [Symbol.iterator](foo: 2): Generator<4, void, undefined>;
  [Symbol.iterator](foo: any): Generator<3 | 4, void, undefined>;
}

const x = [...new Base()];
//    ^? const x: never[]

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Jul 11, 2024
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 5.6.0 milestone Jul 11, 2024
@reverofevil
Copy link
Author

@rbuckton Any news on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants