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