import type {IfUnknown} from './if-unknown'; import type {BuiltIns, LiteralKeyOf} from './internal'; import type {Merge} from './merge'; /** @see PartialOnUndefinedDeep */ export type PartialOnUndefinedDeepOptions = { /** Whether to affect the individual elements of arrays and tuples. @default false */ readonly recurseIntoArrays?: boolean; }; /** Create a deep version of another type where all keys accepting `undefined` type are set to optional. This utility type is recursive, transforming at any level deep. By default, it does not affect arrays and tuples items unless you explicitly pass `{recurseIntoArrays: true}` as the second type argument. Use-cases: - Make all properties of a type that can be undefined optional to not have to specify keys with undefined value. @example ``` import type {PartialOnUndefinedDeep} from 'type-fest'; interface Settings { optionA: string; optionB: number | undefined; subOption: { subOptionA: boolean; subOptionB: boolean | undefined; } }; const testSettings: PartialOnUndefinedDeep = { optionA: 'foo', // 👉 optionB is now optional and can be omitted subOption: { subOptionA: true, // 👉 subOptionB is now optional as well and can be omitted }, }; ``` @category Object */ export type PartialOnUndefinedDeep = T extends Record | undefined ? {[KeyType in keyof T as undefined extends T[KeyType] ? IfUnknown : never]?: PartialOnUndefinedDeepValue} extends infer U // Make a partial type with all value types accepting undefined (and set them optional) ? Merge<{[KeyType in keyof T as KeyType extends LiteralKeyOf ? never : KeyType]: PartialOnUndefinedDeepValue}, U> // Join all remaining keys not treated in U : never // Should not happen : T; /** Utility type to get the value type by key and recursively call `PartialOnUndefinedDeep` to transform sub-objects. */ type PartialOnUndefinedDeepValue = T extends BuiltIns | ((...arguments_: any[]) => unknown) ? T : T extends ReadonlyArray // Test if type is array or tuple ? Options['recurseIntoArrays'] extends true // Check if option is activated ? U[] extends T // Check if array not tuple ? readonly U[] extends T ? ReadonlyArray> // Readonly array treatment : Array> // Mutable array treatment : PartialOnUndefinedDeep<{[Key in keyof T]: PartialOnUndefinedDeep}, Options> // Tuple treatment : T : T extends Record | undefined ? PartialOnUndefinedDeep : unknown;