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
T[] | { t: 'a' } & { t: 'b' } is not iterable (TS2488) #62462
Type 'number[]' must have a '[Symbol.iterator]()' method that returns an iterator.(2488)
🙂 Expected behavior
No error should be present.
Since an object cannot have a field t that holds both 'a' and 'b', { t: 'a' } & { t: 'b' } reduces to never. And number[] | never decays to number[], which is iterable by definition.
As per your typescript code we can not able to assign multiple type values at same time either you assign Array type or an Object type but you can't assign both because of that the issue came.
So, to use such things you can use it like below different ways.
Here you can assign either number[] or { t: 'a' } or { t: 'b' } or [{ t: 'a' } & { t: 'b' }] then it will work.
declare var x: number[] | { t: 'a' } | { t: 'b' }; // Then check if x is array before destructuring if (Array.isArray(x)) { let [elem] = x; }
Also, you can assign value to [elem] like: let [elem] = x as number[];
Using both the way the error has been resolved.
Let me know which approach will feasible for you.
Thank you
Reacted by Oliver Rose, Frog Chen and Ryan Cavanaugh
'a' & 'b' is never so the second union constituent decays to never, and number[] | never decays to number[]. The language server even shows the type as just number[]:
typeFoo=number[]|{t: 'a'}&{t: 'b'}// ^ number[]
I'm not sure this is a bug, but there's some justification for the result to be iterable at least.
Reacted by Martin Johns, Frog Chen, snarbles2, Joe Calzaretta, Matt Kantor, Kisaragi and Nico Rainhart
I expect the issue here is that getReducedType() (as introduced in #36696) doesn't get called before accessing the Symbol.iterator method. They could maybe change that but I thought getReducedType() has sometimes been responsible for compiler performance problems (lots of extra work, circularities leading to crashes, etc)? If so, not sure it's worth it.
It's legitimately confusing that the error describes a situation that is not true.
In TS 7 the improved type-ordering logic causes a more-correct error to be printed, though possibly you could construct a repo where you still see the bad version of the error
a.ts:2:5 - error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator.
2 let [elem] = x
~~~~~~
If there is an extremely minimal fix here we could look at it for the JS codebase but it doesn't seem like a big priority
Why is the problem with the wording of the error message? X | never has all the members that X has, since never is a subtype of X for all X. I mean, the prohibition on indexing into never is more of a linter thing than a safety thing, right? Stopping people from indexing into a pure never makes sense, but stopping them from indexing into a union with never is... well, it seems bonkers to me. The only reason this is showing up at all is because the reduction to number[] is happening too late to avoid the error, but apparently early enough when displaying the type.
So the "more-correct" error talking about never is misleading. Nobody's trying to index into never here. I'd say if there's a fix here it isn't to change the wording of the error, but to remove the error. Or if the wording is changed, it should be to something like error TS252525: This concept of 'number[] | ({ t: 'a' } & { t: 'b' })' confuses and infuriates us!
Trying to remove the error entirely will absolutely introduce new circularity errors in existing codebases. I don't really want to spend breaking change points on addressing a situation that doesn't make sense to be in in the first place.
a situation that doesn't make sense to be in in the first place
The code in the issue description is a minimal working example. FYI, I actually encountered this error in a pretty reasonable setting, I think. Here's the gist:
A third-party package exports a discriminated union type. Let's call it Shape.
And I have a function that deals only with a circle. It accepts either a plain circle object or a React-style [value, setValue] pair. I have to extract the circle type out of the discriminated union through an intersection, which internally introduces { kind: 'rectangle', … } & { kind: 'circle' }. Now the pair cannot be destructured due to type Circle not cleanly reduced.
import{typeShape}from'some-package'typeCircle=Shape&{kind: 'circle'}functiondoStuffWithCircle(arg: Circle|[Circle,(newValue: Circle)=>void]){if(Array.isArray(arg)){let[value,setValue]=arg// error: Type '[{ kind: "circle"; radius: number; } & { kind: "circle"; }, (newValue: { kind: "circle"; radius: number; } & { kind: "circle"; }) => void]' must have a '[Symbol.iterator]()' method that returns an iterator. (ts2488)}}
Reacted by Ryan Cavanaugh, Nathan, snarbles2 and Nico Rainhart
When the arg gets narrowed by getNarrowedTypeWorker (called by narrowTypeByTypePredicate) it still contains that "reduced never" ({ kind: "rectangle"; width: number; height: number; } & { kind: "circle"; }) that isn't displayed in type hovers given it's "never" that shouldn't appear in unions.
and then the filtering logic there doesn't "reject" it because isTypeStrictSubtypeOf(t /* reduced never */, c /* any[] */) // true, so it's kept in the output/narrowed type
that leads to the originally reported situation, given the getIterationTypesOfIterable isn't equipped to deal with those reduced nevers either
If so, would you be open to have a limit on the number of recursive iterations to avoid circularity errors?
I don't see how that helps? When TS encounters a circularity it's effectively in an "If A is true, then A is true" situation where it can't know whether A is true or false; upping that to "If A is true, then if A is true, then A is true" doesn't help that.
Sorry, I may need some clarification about what you mean by circularity errors. I thought we were talking about trying to access the Symbol.iterator method on recursive types. For example:
But that actually works fine, even if I'm calling getReducedType on getIterationTypesOfIterable. Would you mind expanding on what type of circularity errors you're concerned about, and maybe give an example?
I think Ryan Cavanaugh (@RyanCavanaugh) had something in mind about circularities when he considered removing this error entirely. I don't quite think you should be concerned with that.
From what I looked into this, you are on the right track - the fix would boil down to calling getReducedApparentType/getReducedType appropriately. You might have to call it at some point in getIteratedTypeOrElementType though (but maybe you need to land on some combination of calls in getIteratedTypeOrElementType and getIterationTypesOfIterable ).
To validate if your fix is right or not, you can use this test case. Feel free to expand on it - or point out any wrong expected results noted in its comments.
Thanks for the input Mateusz Burzyński (@Andarist)! Calling getReducedType in getIterationTypesOfIterable seems to be enough to fix this. I opened a PR with the proposed fix: #62661
Activity
MartinJohns commentedon Sep 18, 2025
I mean.. having an error is correct. Your type is not iterable. You're not always having an array, you're also having a
{ t: 'a' } & { t: 'b' }But the error message should mention the other type.
urvil5256 commentedon Sep 18, 2025
Hi,
As per your typescript code we can not able to assign multiple type values at same time either you assign Array type or an Object type but you can't assign both because of that the issue came.
So, to use such things you can use it like below different ways.
declare var x: number[] | { t: 'a' } | { t: 'b' }; // Then check if x is array before destructuring if (Array.isArray(x)) { let [elem] = x; }Using both the way the error has been resolved.
Let me know which approach will feasible for you.
Thank you
nmain commentedon Sep 18, 2025
'a' & 'b'isneverso the second union constituent decays tonever, andnumber[] | neverdecays tonumber[]. The language server even shows the type as justnumber[]:I'm not sure this is a bug, but there's some justification for the result to be iterable at least.
satgo1546 commentedon Sep 18, 2025
Yeah, that's also my reasoning behind the bug report. I've edited the issue description to include it for clarity.
jcalz commentedon Sep 18, 2025
(not a TS team member, feel free to ignore me)
I expect the issue here is that
getReducedType()(as introduced in #36696) doesn't get called before accessing theSymbol.iteratormethod. They could maybe change that but I thoughtgetReducedType()has sometimes been responsible for compiler performance problems (lots of extra work, circularities leading to crashes, etc)? If so, not sure it's worth it.RyanCavanaugh commentedon Sep 18, 2025
It's legitimately confusing that the error describes a situation that is not true.
In TS 7 the improved type-ordering logic causes a more-correct error to be printed, though possibly you could construct a repo where you still see the bad version of the error
If there is an extremely minimal fix here we could look at it for the JS codebase but it doesn't seem like a big priority
jcalz commentedon Sep 18, 2025
Why is the problem with the wording of the error message?
X | neverhas all the members thatXhas, sinceneveris a subtype ofXfor allX. I mean, the prohibition on indexing intoneveris more of a linter thing than a safety thing, right? Stopping people from indexing into a purenevermakes sense, but stopping them from indexing into a union withneveris... well, it seems bonkers to me. The only reason this is showing up at all is because the reduction tonumber[]is happening too late to avoid the error, but apparently early enough when displaying the type.So the "more-correct" error talking about
neveris misleading. Nobody's trying to index intoneverhere. I'd say if there's a fix here it isn't to change the wording of the error, but to remove the error. Or if the wording is changed, it should be to something likeerror TS252525: This concept of 'number[] | ({ t: 'a' } & { t: 'b' })' confuses and infuriates us!RyanCavanaugh commentedon Sep 18, 2025
Trying to remove the error entirely will absolutely introduce new circularity errors in existing codebases. I don't really want to spend breaking change points on addressing a situation that doesn't make sense to be in in the first place.
satgo1546 commentedon Sep 19, 2025
The code in the issue description is a minimal working example. FYI, I actually encountered this error in a pretty reasonable setting, I think. Here's the gist:
A third-party package exports a discriminated union type. Let's call it
Shape.And I have a function that deals only with a circle. It accepts either a plain circle object or a React-style
[value, setValue]pair. I have to extract the circle type out of the discriminated union through an intersection, which internally introduces{ kind: 'rectangle', … } & { kind: 'circle' }. Now the pair cannot be destructured due to typeCirclenot cleanly reduced.Andarist commentedon Sep 19, 2025
The above is interesting.
arggets narrowed bygetNarrowedTypeWorker(called bynarrowTypeByTypePredicate) it still contains that "reduced never" ({ kind: "rectangle"; width: number; height: number; } & { kind: "circle"; }) that isn't displayed in type hovers given it's "never" that shouldn't appear in unions.isTypeStrictSubtypeOf(t /* reduced never */, c /* any[] */) // true, so it's kept in the output/narrowed typegetIterationTypesOfIterableisn't equipped to deal with those reduced nevers eithernrainhart commentedon Oct 20, 2025
I confirmed that omitting union members that can be reduced to never in
getIterationTypesOfIterablesolves this issue:function getIterationTypesOfIterable(type: Type, use: IterationUse, errorNode: Node | undefined) { ... for (const constituent of (type as UnionType).types) { + if (!!(getReducedType(constituent).flags & TypeFlags.Never)) { + continue; + } }Ryan Cavanaugh (@RyanCavanaugh) is this the fix you were referring to here?
If so, would you be open to have a limit on the number of recursive iterations to avoid circularity errors?
RyanCavanaugh commentedon Oct 21, 2025
Yes
I don't see how that helps? When TS encounters a circularity it's effectively in an "If A is true, then A is true" situation where it can't know whether
Ais true or false; upping that to "If A is true, then if A is true, then A is true" doesn't help that.nrainhart commentedon Oct 22, 2025
Sorry, I may need some clarification about what you mean by circularity errors. I thought we were talking about trying to access the
Symbol.iteratormethod on recursive types. For example:But that actually works fine, even if I'm calling
getReducedTypeongetIterationTypesOfIterable. Would you mind expanding on what type of circularity errors you're concerned about, and maybe give an example?Andarist commentedon Oct 22, 2025
I think Ryan Cavanaugh (@RyanCavanaugh) had something in mind about circularities when he considered removing this error entirely. I don't quite think you should be concerned with that.
From what I looked into this, you are on the right track - the fix would boil down to calling
getReducedApparentType/getReducedTypeappropriately. You might have to call it at some point ingetIteratedTypeOrElementTypethough (but maybe you need to land on some combination of calls ingetIteratedTypeOrElementTypeandgetIterationTypesOfIterable).To validate if your fix is right or not, you can use this test case. Feel free to expand on it - or point out any wrong expected results noted in its comments.
nrainhart commentedon Oct 22, 2025
Thanks for the input Mateusz Burzyński (@Andarist)! Calling
getReducedTypeingetIterationTypesOfIterableseems to be enough to fix this. I opened a PR with the proposed fix: #62661