So, I might not have the right terminology to describe this problem, which made searching for it tricky, but I hope my example helps clear it up.
Context: I'm using query data selectors in react-query to preprocess query results and attach some properties that I need globally in the application. I'm running into an issue with the produced types, which I managed to narrow down and reproduce outside of react-query itself.
Here's the reproduction code (also on TS Playground)
// Data structure coming from third-party code (I have no control over it)
// Simplified for a more concise reproduction:
type DataStructure =
& Array<{ a: number; b: number }>
& Array<{ b: number; c: number }>
const structure: DataStructure = [
{
a: 1,
b: 2,
c: 3,
},
]
structure[0].a // ok
structure[0].b // ok
structure[0].c // ok
for (const s of structure) {
s.a // ok
s.b // ok
s.c // ok
}
structure.forEach(s => {
s.a // ok
s.b // ok
s.c // Property 'c' does not exist on type '{ a: number; b: number; }'.(2339)
})
// If we had control over DataStructure, we'd write it like this instead:
// Array<{ a: number; b: number } & { b: number; c: number }>
The DataStructure
is an intersection type of two Arrays with partially overlapping item types.
As demonstrated by the code and the comments, all 3 properties are available when the array items are accessed either by their index or inside a for
loop, but inside a forEach
loop (or any array method like some
, every
, etc,) only properties of the first array type (from the intersection) are available.
Trying to access c
inside the forEach
loop, TypeScript complains:
Property 'c' does not exist on type '{ a: number; b: number; }'.(2339)
Now, if I had control over the data structure definition, I'd describe it like this:
type DataStrcture = Array<{ a: number; b: number } & { b: number; c: number }>
That would indeed solve the issue. But I don't have control over that part of the code.
What I'm more interested in is understanding WHY TypeScript behaves the way it does here. That's what's baffling me the most, but if someone can offer a clean solution, too, that'd be extra amazing!