1 | import type {BuiltIns} from './internal';
|
2 | import type {Merge} from './merge';
|
3 |
|
4 | /**
|
5 | Create a deep version of another type where all optional keys are set to also accept `undefined`.
|
6 |
|
7 | Note: This is only needed when the [`exactOptionalPropertyTypes`](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes) TSConfig setting is enabled.
|
8 |
|
9 | Use-cases:
|
10 | - When `exactOptionalPropertyTypes` is enabled, an object like `{a: undefined}` is not assignable to the type `{a?: number}`. You can use `UndefinedOnPartialDeep<{a?: number}>` to make it assignable.
|
11 |
|
12 | @example
|
13 | ```
|
14 | import type {UndefinedOnPartialDeep} from 'type-fest';
|
15 |
|
16 | interface Settings {
|
17 | optionA: string;
|
18 | optionB?: number;
|
19 | subOption: {
|
20 | subOptionA: boolean;
|
21 | subOptionB?: boolean;
|
22 | }
|
23 | };
|
24 |
|
25 | const testSettingsA: Settings = {
|
26 | optionA: 'foo',
|
27 | optionB: undefined, // TypeScript error if `exactOptionalPropertyTypes` is true.
|
28 | subOption: {
|
29 | subOptionA: true,
|
30 | subOptionB: undefined, // TypeScript error if `exactOptionalPropertyTypes` is true
|
31 | },
|
32 | };
|
33 |
|
34 | const testSettingsB: UndefinedOnPartialDeep<Settings> = {
|
35 | optionA: 'foo',
|
36 | optionB: undefined, // 👉 `optionB` can be set to undefined now.
|
37 | subOption: {
|
38 | subOptionA: true,
|
39 | subOptionB: undefined, // 👉 `subOptionB` can be set to undefined now.
|
40 | },
|
41 | };
|
42 | ```
|
43 | */
|
44 | export type UndefinedOnPartialDeep<T> =
|
45 | // Handle built-in type and function
|
46 | T extends BuiltIns | Function
|
47 | ? T
|
48 | // Handle tuple and array
|
49 | : T extends readonly unknown[]
|
50 | ? UndefinedOnPartialList<T>
|
51 | // Handle map and readonly map
|
52 | : T extends Map<infer K, infer V>
|
53 | ? Map<K, UndefinedOnPartialDeep<V>>
|
54 | : T extends ReadonlyMap<infer K, infer V>
|
55 | ? ReadonlyMap<K, UndefinedOnPartialDeep<V>>
|
56 | // Handle set and readonly set
|
57 | : T extends Set<infer K>
|
58 | ? Set<UndefinedOnPartialDeep<K>>
|
59 | : T extends ReadonlySet<infer K>
|
60 | ? ReadonlySet<UndefinedOnPartialDeep<K>>
|
61 | // Handle object
|
62 | : T extends Record<any, any>
|
63 | ? {
|
64 | [KeyType in keyof T]: undefined extends T[KeyType]
|
65 | ? UndefinedOnPartialDeep<T[KeyType]> | undefined
|
66 | : UndefinedOnPartialDeep<T[KeyType]>
|
67 | }
|
68 | : T; // If T is not builtins / function / array / map / set / object, return T
|
69 |
|
70 | // Handle tuples and arrays
|
71 | type UndefinedOnPartialList<T extends readonly unknown[]> = T extends []
|
72 | ? []
|
73 | : T extends [infer F, ...infer R]
|
74 | ? [UndefinedOnPartialDeep<F>, ...UndefinedOnPartialDeep<R>]
|
75 | : T extends readonly [infer F, ...infer R]
|
76 | ? readonly [UndefinedOnPartialDeep<F>, ...UndefinedOnPartialDeep<R>]
|
77 | : T extends Array<infer F>
|
78 | ? Array<UndefinedOnPartialDeep<F>>
|
79 | : T extends ReadonlyArray<infer F>
|
80 | ? ReadonlyArray<UndefinedOnPartialDeep<F>>
|
81 | : never;
|