Reputation: 61
I'm having a problem understanding unions in TS, why is the below a valid assignment? I thought it would only be valid for const a = {a:12}
or {a:123,b:23}
or {a:12,b:12,c:123}
.
type abcd =
| {
a: number;
}
| {
a: number;
b: number;
}
| {
a: number;
b: number;
c: number;
};
const a: abcd = {
a:123,
c:234
};
It doesn't allow the assignment if I change c
to somethingElse
:
const a: abcd = {
a:123,
somethingElse:234 // Error on this line
};
It gives me:
Type '{ a: number; somethingElse: number; }' is not assignable to type 'abcd'. Object literal may only specify known properties, and 'somethingElse' does not exist in type 'abcd'.(2322)
Upvotes: 6
Views: 746
Reputation: 839
TLDR
Unions are not mutually exclusive. You can check the Work around section in this answer for a work around to achieve mutual exclusion for types with a combination of Union type, never type, and optional property.
The way that the Union type work is more closer to the OR
gate rather than the XOR
gate. That is, as long as the instance object satisfies one of the types in your Union type, it is considered an instance of that Union type.
Take the following for example:
type abcd =
| {
a: number;
}
| {
x: number;
y: number;
}
| {
p: number;
q: number;
r: number;
}
| {
x: number;
y: number;
};
const APY: abcd = {
a: 123,
p: 234,
y: 1,
};
as the constant APY
contains enough properties to be considered as an instance of { a: number; }
type from your union, all the other properties which can be present in the Union type are allowed.
The only limitation imposed by Union type is that those properties are not allowed which are not present in the Union type and the instance must have enough properties to satisfy one of the types present in union.
Work around
While the Union type itself will not help you, you can use it in conjunction with never type to achieve your desired result:
type abcd =
| {
a: number;
b?: never;
c?: never;
}
| {
a: number;
b: number;
c?: never;
}
| {
a: number;
b: number;
c: number;
};
/*
Type '{ a: number; c: number; }' is not assignable to type 'abcd'.
Property 'b' is missing in type '{ a: number; c: number; }' but required in type '{ a: number; b: number; c: number; }'.
*/
const a: abcd = {
a: 123,
c: 234,
};
Upvotes: 3