Open reverofevil opened 3 months ago
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.
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.
The proper fix for this is to thread the obtained [Symbol.iterator
] signatures through resolveCall
. Just like this PR does things with [Symbol.hasInstance]
: https://github.com/microsoft/TypeScript/pull/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
}
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[]
π Search Terms
generic generator static
π Version & Regression Information
β― Playground Link
Link
π» Code
π 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
On the other hand, this is a nice way to create unique types for opaque type implementation! π