-
-
Notifications
You must be signed in to change notification settings - Fork 546
/
pick-deep.d.ts
149 lines (136 loc) · 4.05 KB
/
pick-deep.d.ts
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import type {BuildObject, BuildTuple, NonRecursiveType, ObjectValue} from './internal';
import type {IsNever} from './is-never';
import type {Paths} from './paths';
import type {Simplify} from './simplify.d';
import type {UnionToIntersection} from './union-to-intersection.d';
import type {UnknownArray} from './unknown-array';
/**
Pick properties from a deeply-nested object.
It supports recursing into arrays.
Use-case: Distill complex objects down to the components you need to target.
@example
```
import type {PickDeep, PartialDeep} from 'type-fest';
type Configuration = {
userConfig: {
name: string;
age: number;
address: [
{
city1: string;
street1: string;
},
{
city2: string;
street2: string;
}
]
};
otherConfig: any;
};
type NameConfig = PickDeep<Configuration, 'userConfig.name'>;
// type NameConfig = {
// userConfig: {
// name: string;
// }
// };
// Supports optional properties
type User = PickDeep<PartialDeep<Configuration>, 'userConfig.name' | 'userConfig.age'>;
// type User = {
// userConfig?: {
// name?: string;
// age?: number;
// };
// };
// Supports array
type AddressConfig = PickDeep<Configuration, 'userConfig.address.0'>;
// type AddressConfig = {
// userConfig: {
// address: [{
// city1: string;
// street1: string;
// }];
// };
// }
// Supports recurse into array
type Street = PickDeep<Configuration, 'userConfig.address.1.street2'>;
// type Street = {
// userConfig: {
// address: [
// unknown,
// {street2: string}
// ];
// };
// }
```
@category Object
@category Array
*/
export type PickDeep<T, PathUnion extends Paths<T>> =
T extends NonRecursiveType
? never
: T extends UnknownArray
? UnionToIntersection<{
[P in PathUnion]: InternalPickDeep<T, P>;
}[PathUnion]
>
: T extends object
? Simplify<UnionToIntersection<{
[P in PathUnion]: InternalPickDeep<T, P>;
}[PathUnion]>>
: never;
/**
Pick an object/array from the given object/array by one path.
*/
type InternalPickDeep<T, Path extends string | number> =
T extends NonRecursiveType
? never
: T extends UnknownArray ? PickDeepArray<T, Path>
: T extends object ? Simplify<PickDeepObject<T, Path>>
: never;
/**
Pick an object from the given object by one path.
*/
type PickDeepObject<RecordType extends object, P extends string | number> =
P extends `${infer RecordKeyInPath}.${infer SubPath}`
? ObjectValue<RecordType, RecordKeyInPath> extends infer ObjectV
? IsNever<ObjectV> extends false
? BuildObject<RecordKeyInPath, InternalPickDeep<NonNullable<ObjectV>, SubPath>, RecordType>
: never
: never
: ObjectValue<RecordType, P> extends infer ObjectV
? IsNever<ObjectV> extends false
? BuildObject<P, ObjectV, RecordType>
: never
: never;
/**
Pick an array from the given array by one path.
*/
type PickDeepArray<ArrayType extends UnknownArray, P extends string | number> =
// Handle paths that are `${number}.${string}`
P extends `${infer ArrayIndex extends number}.${infer SubPath}`
// When `ArrayIndex` is equal to `number`
? number extends ArrayIndex
? ArrayType extends unknown[]
? Array<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
: ArrayType extends readonly unknown[]
? ReadonlyArray<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
: never
// When `ArrayIndex` is a number literal
: ArrayType extends unknown[]
? [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
: ArrayType extends readonly unknown[]
? readonly [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
: never
// When the path is equal to `number`
: P extends `${infer ArrayIndex extends number}`
// When `ArrayIndex` is `number`
? number extends ArrayIndex
? ArrayType
// When `ArrayIndex` is a number literal
: ArrayType extends unknown[]
? [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
: ArrayType extends readonly unknown[]
? readonly [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
: never
: never;