import type {If} from './if.d.ts'; import type {IfNotAnyOrNever, MapsSetsOrArrays, NonRecursiveType} from './internal/type.d.ts'; import type {IsUnknown} from './is-unknown.d.ts'; import type {KeysOfUnion} from './keys-of-union.d.ts'; import type {Simplify} from './simplify.d.ts'; /** Ensure mutual exclusivity in object unions by adding other members’ keys as `?: never`. Use-cases: - You want each union member to be exclusive, preventing overlapping object shapes. - You want to safely access any property defined across the union without additional type guards. @example ``` import type {ExclusifyUnion} from 'type-fest'; type FileConfig = { filePath: string; }; type InlineConfig = { content: string; }; declare function loadConfig1(options: FileConfig | InlineConfig): void; // Someone could mistakenly provide both `filePath` and `content`. loadConfig1({filePath: './config.json', content: '{ "name": "app" }'}); // No errors // Use `ExclusifyUnion` to prevent that mistake. type Config = ExclusifyUnion; //=> {filePath: string; content?: never} | {content: string; filePath?: never} declare function loadConfig2(options: Config): void; // @ts-expect-error loadConfig2({filePath: './config.json', content: '{ "name": "app" }'}); //=> Error: Argument of type '{ filePath: string; content: string; }' is not assignable to parameter of type '{ filePath: string; content?: never; } | { content: string; filePath?: never; }'. loadConfig2({filePath: './config.json'}); // Ok loadConfig2({content: '{ "name": "app" }'}); // Ok ``` @example ``` import type {ExclusifyUnion} from 'type-fest'; type CardPayment = { amount: number; cardNumber: string; }; type PaypalPayment = { amount: number; paypalId: string; }; function processPayment1(payment: CardPayment | PaypalPayment) { // @ts-expect-error const details = payment.cardNumber ?? payment.paypalId; // Cannot access `cardNumber` or `paypalId` directly } type Payment = ExclusifyUnion; //=> {amount: number; cardNumber: string; paypalId?: never} | {amount: number; paypalId: string; cardNumber?: never} function processPayment2(payment: Payment) { const details = payment.cardNumber ?? payment.paypalId; // Ok //=> string } ``` @example ``` import type {ExclusifyUnion} from 'type-fest'; type A = ExclusifyUnion<{a: string} | {b: number}>; //=> {a: string; b?: never} | {a?: never; b: number} type B = ExclusifyUnion<{a: string} | {b: number} | {c: boolean}>; //=> {a: string; b?: never; c?: never} | {a?: never; b: number; c?: never} | {a?: never; b?: never; c: boolean} type C = ExclusifyUnion<{a: string; b: number} | {b: string; c: number}>; //=> {a: string; b: number; c?: never} | {a?: never; b: string; c: number} type D = ExclusifyUnion<{a?: 1; readonly b: 2} | {d: 4}>; //=> {a?: 1; readonly b: 2; d?: never} | {a?: never; b?: never; d: 4} ``` @category Object @category Union */ export type ExclusifyUnion = IfNotAnyOrNever, Union, Extract extends infer SkippedMembers ? SkippedMembers | _ExclusifyUnion> : never > >; type _ExclusifyUnion = Union extends unknown // For distributing `Union` ? Simplify< Union & Partial< Record< Exclude, keyof Union>, never > > > : never; // Should never happen export {};