typescript / expert
Snippet
Mutual Exclusion Types via Distributive Conditional Filtering
Standard TypeScript unions allow objects containing properties from both constituent interfaces. The XOR utility construct enforces strict mutual exclusion by using mapped types with optional 'never' properties. This prevents users from supplying keys belonging to interface U when providing interface T, enforcing true exclusive-OR semantics.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };type XOR<T, U> = (T | U) extends object? (Without<T, U> & U) | (Without<U, T> & T): T | U;interface CreditCardPayment {cardNumber: string;cvv: string;}interface PayPalPayment {paypalEmail: string;authToken: string;}type PaymentMethod = XOR<CreditCardPayment, PayPalPayment>;function processPayment(payment: PaymentMethod): void {if ('cardNumber' in payment) {console.log(`Processing card ${payment.cardNumber}`);} else {console.log(`Processing PayPal ${payment.paypalEmail}`);}}
Breakdown
1
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
Creates a type where keys present in T but absent in U are explicitly mapped to optional 'never'.
2
type XOR<T, U> = (T | U) extends object
Distributes over object types to form mutually exclusive intersected variants.
3
type PaymentMethod = XOR<CreditCardPayment, PayPalPayment>;
Disallows supplying both PayPal and Credit Card fields simultaneously in a single payment object.
4
if ('cardNumber' in payment)
Serves as a type guard to safely narrow the XOR union to a specific payment branch.