I've got two types that are both an array of objects, with their fields, in an intersection type in Typescript.
If I take an element from the array I can access the second field, but if I use an array method (forEach, map, filter etc) the compiler doesn't work.
Is there a particular reason why in a case work but fails in the other?
Here a sample code:
var obj: {
foo?: string;
}[] & {
bar?: boolean;
}[] = [{foo: "a", bar: true}];
obj.forEach((q) => q.bar); // bar does not exist
obj.map((q) => q.bar) // bar does not exist
obj[0].bar; // OK
CodePudding user response:
Per Ryan Cavanaugh (active member fo the TypeScript team), it is a bad idea to have an intersection of 2 array types.
This happens because we just merge the signatures of forEach in order, whereas element access produces the recursively merged property type. You could specify a type annotation on item, or write type foo3 = foo2 & foo1 (!).
But in general it's really unclear how type TU = T[] & U[] should be interpreted -- is it a heterogeneous array, the same as Array<T | U> ? Or the same as Array<T & U> ? An axiom is that for any operation x.y(z), that should still be legal for x if x becomes an intersection type containing the original constituent, but that wouldn't be true if we interpreted the array to be Array<T & U> (since z would have to be the intersected element type as well).
So overall... probably a bad idea to have the type T[] & U[] floating around.
What you probably should have is Array<{ foo?: string;} & {bar?: boolean}>
CodePudding user response:
It would be more obvious to understand if you write down array types:
type ItemFoo = {
foo?: string;
};
type ArrFoo = ItemFoo[];
type ItemBar = {
bar?: boolean;
};
type ArrBar = ItemBar[];
const obj: ArrFoo & ArrBar = [{foo: "a", bar: true}];
So you can notice ArrFoo
has forEach(cb: (x: ItemFoo, i?) => void)
method, and ArrBar
has forEach(cb: (x:ItemBar, i?) => void)
method. And when the type merge happens, only one signature is taken.
If you have access just to the ArrFoo
and ArrBar
types, make the intersection of item types
const obj: (ArrFoo[0] & ArrBar[0])[] = [{foo: "a", bar: true}];
Or with the Helper Type.
type ArrIntersection<T1 extends Array<any>, T2 extends Array<any>> = (T1[0] & T2[0])[];
const obj: ArrIntersection<ArrFoo, ArrBar> = [{foo: "a", bar: true}];