Indeed, the documentation does not provide a clear explanation of operator precedence in the type definition. However, one can easily test it:
type Z = { a: number }[] & { b: string }[];
let a: Z = [];
a = [{ a: 1, b: "q" }]; // This assignment is invalid because `b` does not exist in `{ a: number }`.
a = [{ a: 1 }]; // This assignment is also invalid because `a` does not exist in `{ b: string }`.
a = [{ b: "foo" }]; // Another invalid assignment due to `b` not existing in `{ a: number }`.
a = []; // This assignment is valid.
let x: { a: number, b: string }[] = a; // This assignment is also invalid.
The results show that the assumption it resolves to { a:number,b:string }
is incorrect. Initially, I assumed it would resolve to
({ a: number } | { b: string })[]
, but that's inaccurate too.
The intersection denotes "an array of elements where each element satisfies the type { a: number }
and simultaneously satisfies the type { b: string }
. Since a
is absent in the second type and b
is missing in the first, no element can fulfill the intersection. Therefore, only empty arrays can be assigned to variables typed with the intersection.
To achieve the expected outcome, a different intersection is required:
type Z2 = { a: number, b?: string }[] & { a?: number, b: string }[];
let a2: Z2 = [];
a2 = [{ a: 1, b: "q" }];
This will work as anticipated because the first type in the intersection allows elements that may optionally have b
, while the second type allows elements that may optionally have a
.