Consider the next code:
type unionType = (A | B)[]
type unionArr = A[] | B[]
Is there is a difference between those two options?
CodePudding user response:
There is a difference. (A | B)[]
represents an array that can store items of type A
and type B
. A[] | B[]
represents an array where all items are either of type A
or all items are of type B
class A { a = "a" }
class B { b = "b" }
type unionType = (A | B)[]
let ut: unionType;
ut = [new A(), new A()]
ut = [new B(), new B()]
ut = [new A(), new B()] // mixed array is fine
let uti = ut[0] // Reading we get A | B beacuse anythign can come out of the array
ut[0] = new B();
ut[1] = new A();
ut.reduce((s, e) => e instanceof A ? e.a s: e.b s, "") // ok
type unionArr = A[] | B[]
let uArr: unionArr;
uArr = [new A(), new A()]
uArr = [new B(), new B()]
uArr = [new A(), new B()] // mixed array is not fine
let uArri = uArr[0] // Reading we get A | B beacuse we don't kow if we have A[] or B[]
// Type gap: We can actually assign a B into uArr[0] event if it is holding a A[]
uArr[0] = new B();
uArr[1] = new A();
// err This expression is not callable.
uArr.reduce((s, e) => e instanceof A ? e.a s: e.b s, "")
For A[] | B[]
, the constraint that the array items must all be A
or all must all be B
is only really checked on assignment of the array, not on assigning to an index of an array unfortunately.
Also the way call signatures work for unions, you might end up with un-callable functions for A[] | B[]
, for example reduce
(map for example will work)
I would generally stick with (A | B)[]
unless there is a good reason for the other that outweighs it's problems.