I have an interface (shown below). Currently all properties are required in it. How can I make it so that only one property is required. Basically the properties are mutually exclusive so for example ff a ‘top’ property is selected then no other property can be selected.
interface PaddingOptions {
'top': number;
'bottom': number;
'left': number;
'right': number;
}
const foo: PaddingOptions = {top: 20, bottom: 20} //this should give error as both top and bottom can not be used
const foo: PaddingOptions = {right: 20}; // ok as only one property is selected
Advertisement
Answer
Well, may be it’s me, but @Zain Zafar’s answer does not fit, since XOR<,>
like he defined is constrained to only two type arguments.
Extending that XOR<,>
type to more type arguments would be quite verbose and not even flexible.
Following the same article where the XOR<,>
type came, though, there was an alternative which looks a bit more flexible, the OneOf<,>
.
OneOf<,>
defines the same solution as XOR<,>
but expecting only one type definition with no recursive typing (like XOR<,>
needs).
To fit the problem by using XOR<,>
we would need something like so
type PaddingOptions = XOR<{right: number},
XOR<{left: number},
XOR<{bottom: number}, {top: number}>>>;
Which becomes really unpleasant to read.
OneOf<,>
becomes quite trickier to read, but easy to implement
type OneOf<T, K extends keyof T> = {
[Key in K]: Pick<Required<T>, Key> & {
[InnerKey in Exclude<K, Key>]?: never;
};
}[K];
Well, I just wiped out the Omit<T, K> & ...
which in our case will result to never & ...
(redundant), because we are passing all keys from T
.
So, implemeting OneOf<,>
for the question case is as easy as
type PaddingOptionKeys = 'top' | 'right' | 'bottom' | 'left';
type PaddingOptions = OneOf<{ [Key in PaddingOptionKeys]: number; }, PaddingOptionKeys>;
const option: PaddingOptions = { left: 9 }; // OK
const option2: PaddingOptions = { right: 9 }; // Still OK
const option3: PaddingOptions = { right: 9, left: 0 }; // Throws error
Hence, by using OneOf<,>
we constraint multiple keys in a comfortably way, easy to read and flexible.
Hope it helps.