You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Implicit Symbol.iterator call in for..of loops / spread destructuring doesn't infer this generic type parameter #38388
Search Terms: Symbol.iterator type parameter implicit calling for..of loops object spread destructuring this generic
Code
What follows is 3 different ways of iterating over an object using a custom-made iterator. However, using Symbol.iterator implicitly via the for..of loop fails to produce the same behavior:
constobj={x: 1,y: 2,z: 3,*[Symbol.iterator]<Textendsobject>(this: T,): Generator<NonNullable<{[KinkeyofT]: [K,NonNullable<T[K]>]}[keyofT]>,void,unknown>{for(constentryofObject.entries(this))yieldentryasnever;},};{constiter=obj[Symbol.iterator]();for(letresult=iter.next();!result.done;result=iter.next()){const{ value }=result;value;// ["x", number] | ["y", number] | ["z", number]}}for(constvalueofobj[Symbol.iterator]()){value;// ["x", number] | ["y", number] | ["z", number]}for(constvalueofobj){value;// NonNullable<{ [K in keyof T]: [K, NonNullable<T[K]>]; }[keyof T]>// bad! This should be ["x", number] | ["y", number] | ["z", number]}// also, spread destructuring suffers from the same problem!constvalues=[...obj];// Array<NonNullable<{ [K in keyof T]: [K, NonNullable<T[K]>]; }[keyof T]>>// bad! This should be Array<["x", number] | ["y", number] | ["z", number]>
Expected behavior:for (const value of obj) should implicitly do the type equivalent of calling our iterator symbol, i.e. for (const value of obj[Symbol.iterator]()). That means our type parameter should be correctly inferred like so: obj[Symbol.iterator]<T>(this: T).
Actual behavior:T cannot be inferred as the local this type, so all derivative types cannot be evaluated and the type stays as its generic version.
I did my best to track down this problem on my own, and I believe the problem stems from this bit of code. However, I don't know the codebase well enough to know where to go from here:
// file: src/compiler/checker.ts/** * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like * type from its members. * * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` * record is returned. Otherwise, `noIterationTypes` is returned. * * NOTE: You probably don't want to call this directly and should be calling * `getIterationTypesOfIterable` instead. */functiongetIterationTypesOfIterableSlow(type: Type,resolver: IterationTypesResolver,errorNode: Node|undefined){constmethod=getPropertyOfType(type,getPropertyNameForKnownSymbolName(resolver.iteratorSymbolName));constmethodType=method&&!(method.flags&SymbolFlags.Optional) ? getTypeOfSymbol(method) : undefined;if(isTypeAny(methodType)){returnsetCachedIterationTypes(type,resolver.iterableCacheKey,anyIterationTypes);}constsignatures=methodType ? getSignaturesOfType(methodType,SignatureKind.Call) : undefined;if(!some(signatures)){returnsetCachedIterationTypes(type,resolver.iterableCacheKey,noIterationTypes);}constiteratorType=getUnionType(map(signatures,getReturnTypeOfSignature),UnionReduction.Subtype);constiterationTypes=getIterationTypesOfIterator(iteratorType,resolver,errorNode)??noIterationTypes;returnsetCachedIterationTypes(type,resolver.iterableCacheKey,iterationTypes);}
I think the problem is that map(signatures, getReturnTypeOfSignature) is used to grab the (raw) return type(s) and that neglects to account for the possibility of there being an inferred this parameter.
Consider this (simpler) code:
constobj={x: 1,y: 2,z: 3,*[Symbol.iterator]<Pextendsobject>(this: P): Generator<keyofP>{for(constkinthis)yieldk;},};for(constvalueofobj){console.log(value);// value is `keyof P`, but should be `"x" | "y" | "z"`}
Here are the relevant variables in getIterationTypesOfIterableSlow when inferring value from for (const value of obj)
I am guessing that assignParameterType or assignContextualParameterTypes may have something to do with the solution, but again, I really have no idea. Please help!
changed the title [-]Implicit Symbol.iterator call in for..of loops and object spreading doesn't infer type parameter(s)[/-][+]Implicit Symbol.iterator call in for..of loops / spread destructuring doesn't infer `this` generic type parameter[/+]on May 31, 2020
We'd take a PR on this so long as it's not invasive, but it doesn't seem like this is a high priority right now due to the low volume of feedback we've seen on it.
It complains about this.registry not existing but it does exist and the code actually runs fine. I worked around it by simplifying the code but was surprised TS lost track of this.
typeArrayIteratorMethod=<ThisextendsArrayLike<any>>(this: This)=>IterableIterator<This[number]>typeMyStringArray={[k:number]: string,length: number,[Symbol.iterator]: ArrayIteratorMethod}consty=['a','b','c']asMyStringArrayfor(constbofy[Symbol.iterator]()){// const b:string// ^?bsatisfiesstring// @ts-expect-errorbsatisfiessymbol}for(constbofy){// const b: This[number] (should be inferred as string, but instead `This` escapes from scope)// ^?bsatisfiesstring// so this unexpectedly passes, since the type of 'b' behaves like `any`// @ts-expect-errorbsatisfiessymbol}
👋 Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of this repro running against the nightly TypeScript.
‼️ Exception: Error - error TS5107: Option 'moduleResolution=node10' is deprecated and will stop functioning in TypeScript 7.0. Specify compilerOption '"ignoreDeprecations": "6.0"' to silence this error.
Visit https://aka.ms/ts6 for migration information.
Error: error TS5107: Option 'moduleResolution=node10' is deprecated and will stop functioning in TypeScript 7.0. Specify compilerOption '"ignoreDeprecations": "6.0"' to silence this error.
Visit https://aka.ms/ts6 for migration information.
at Object.createVirtualTypeScriptEnvironment (/home/runner/work/_actions/microsoft/TypeScript-Twoslash-Repro-Action/master/dist/index.js:8128:11)
at twoslasher (/home/runner/work/_actions/microsoft/TypeScript-Twoslash-Repro-Action/master/dist/index.js:7618:17)
at /home/runner/work/_actions/microsoft/TypeScript-Twoslash-Repro-Action/master/dist/index.js:439:44
at runTwoslashRequests (/home/runner/work/_actions/microsoft/TypeScript-Twoslash-Repro-Action/master/dist/index.js:406:56)
at run (/home/runner/work/_actions/microsoft/TypeScript-Twoslash-Repro-Action/master/dist/index.js:20096:75)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Activity
Validark commentedon May 31, 2020
I did my best to track down this problem on my own, and I believe the problem stems from this bit of code. However, I don't know the codebase well enough to know where to go from here:
I think the problem is that
map(signatures, getReturnTypeOfSignature)is used to grab the (raw) return type(s) and that neglects to account for the possibility of there being an inferredthisparameter.Consider this (simpler) code:
Here are the relevant variables in
getIterationTypesOfIterableSlowwhen inferringvaluefromfor (const value of obj)I am guessing that
assignParameterTypeorassignContextualParameterTypesmay have something to do with the solution, but again, I really have no idea. Please help![-]Implicit Symbol.iterator call in for..of loops and object spreading doesn't infer type parameter(s)[/-][+]Implicit Symbol.iterator call in for..of loops / spread destructuring doesn't infer `this` generic type parameter[/+]Validark commentedon Jun 21, 2020
I fixed it!! I'll submit a PR soon!

Validark commentedon Oct 2, 2020
Sorry for dropping off the face of the earth. I'm back in the game! My next priority and current goal is to get this PR out this week.
Validark commentedon Oct 26, 2020
Update: I've been putting a bunch of hours into this, I hope I am almost finished with it now.
13 remaining items
RyanCavanaugh commentedon Feb 4, 2022
We'd take a PR on this so long as it's not invasive, but it doesn't seem like this is a high priority right now due to the low volume of feedback we've seen on it.
greggman commentedon Dec 30, 2022
I think I ran into this issue recently. I had code like this
It complains about this.registry not existing but it does exist and the code actually runs fine. I worked around it by simplifying the code but was surprised TS lost track of
this.rotu commentedon Jan 26, 2024
This also causes a leak of type parameters:
Workbench Repro
typescript-bot commentedon Feb 15, 2024
👋 Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of this repro running against the nightly TypeScript.
Comment by Dan Rose (@rotu)
Historical Information
❌ Failed: -
Unused '@ts-expect-error' directive.