@visulima/object 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/LICENSE.md +124 -0
- package/README.md +140 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +2309 -0
- package/dist/index.d.mts +2309 -0
- package/dist/index.d.ts +2309 -0
- package/dist/index.mjs +1 -0
- package/package.json +144 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,2309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
|
|
3
|
+
|
|
4
|
+
@category Type
|
|
5
|
+
*/
|
|
6
|
+
type Primitive =
|
|
7
|
+
| null
|
|
8
|
+
| undefined
|
|
9
|
+
| string
|
|
10
|
+
| number
|
|
11
|
+
| boolean
|
|
12
|
+
| symbol
|
|
13
|
+
| bigint;
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- It has to be an `interface` so that it can be merged.
|
|
17
|
+
interface SymbolConstructor {
|
|
18
|
+
readonly observable: symbol;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare const emptyObjectSymbol: unique symbol;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
Represents a strictly empty plain object, the `{}` value.
|
|
26
|
+
|
|
27
|
+
When you annotate something as the type `{}`, it can be anything except `null` and `undefined`. This means that you cannot use `{}` to represent an empty plain object ([read more](https://stackoverflow.com/questions/47339869/typescript-empty-object-and-any-difference/52193484#52193484)).
|
|
28
|
+
|
|
29
|
+
@example
|
|
30
|
+
```
|
|
31
|
+
import type {EmptyObject} from 'type-fest';
|
|
32
|
+
|
|
33
|
+
// The following illustrates the problem with `{}`.
|
|
34
|
+
const foo1: {} = {}; // Pass
|
|
35
|
+
const foo2: {} = []; // Pass
|
|
36
|
+
const foo3: {} = 42; // Pass
|
|
37
|
+
const foo4: {} = {a: 1}; // Pass
|
|
38
|
+
|
|
39
|
+
// With `EmptyObject` only the first case is valid.
|
|
40
|
+
const bar1: EmptyObject = {}; // Pass
|
|
41
|
+
const bar2: EmptyObject = 42; // Fail
|
|
42
|
+
const bar3: EmptyObject = []; // Fail
|
|
43
|
+
const bar4: EmptyObject = {a: 1}; // Fail
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Unfortunately, `Record<string, never>`, `Record<keyof any, never>` and `Record<never, never>` do not work. See {@link https://github.com/sindresorhus/type-fest/issues/395 #395}.
|
|
47
|
+
|
|
48
|
+
@category Object
|
|
49
|
+
*/
|
|
50
|
+
type EmptyObject = {[emptyObjectSymbol]?: never};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
Returns a boolean for whether the two given types are equal.
|
|
54
|
+
|
|
55
|
+
@link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
|
|
56
|
+
@link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796
|
|
57
|
+
|
|
58
|
+
Use-cases:
|
|
59
|
+
- If you want to make a conditional branch based on the result of a comparison of two types.
|
|
60
|
+
|
|
61
|
+
@example
|
|
62
|
+
```
|
|
63
|
+
import type {IsEqual} from 'type-fest';
|
|
64
|
+
|
|
65
|
+
// This type returns a boolean for whether the given array includes the given item.
|
|
66
|
+
// `IsEqual` is used to compare the given array at position 0 and the given item and then return true if they are equal.
|
|
67
|
+
type Includes<Value extends readonly any[], Item> =
|
|
68
|
+
Value extends readonly [Value[0], ...infer rest]
|
|
69
|
+
? IsEqual<Value[0], Item> extends true
|
|
70
|
+
? true
|
|
71
|
+
: Includes<rest, Item>
|
|
72
|
+
: false;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
@category Type Guard
|
|
76
|
+
@category Utilities
|
|
77
|
+
*/
|
|
78
|
+
type IsEqual<A, B> =
|
|
79
|
+
(<G>() => G extends A ? 1 : 2) extends
|
|
80
|
+
(<G>() => G extends B ? 1 : 2)
|
|
81
|
+
? true
|
|
82
|
+
: false;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
Represents an array with `unknown` value.
|
|
86
|
+
|
|
87
|
+
Use case: You want a type that all arrays can be assigned to, but you don't care about the value.
|
|
88
|
+
|
|
89
|
+
@example
|
|
90
|
+
```
|
|
91
|
+
import type {UnknownArray} from 'type-fest';
|
|
92
|
+
|
|
93
|
+
type IsArray<T> = T extends UnknownArray ? true : false;
|
|
94
|
+
|
|
95
|
+
type A = IsArray<['foo']>;
|
|
96
|
+
//=> true
|
|
97
|
+
|
|
98
|
+
type B = IsArray<readonly number[]>;
|
|
99
|
+
//=> true
|
|
100
|
+
|
|
101
|
+
type C = IsArray<string>;
|
|
102
|
+
//=> false
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
@category Type
|
|
106
|
+
@category Array
|
|
107
|
+
*/
|
|
108
|
+
type UnknownArray = readonly unknown[];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
|
|
112
|
+
|
|
113
|
+
@example
|
|
114
|
+
```
|
|
115
|
+
import type {Simplify} from 'type-fest';
|
|
116
|
+
|
|
117
|
+
type PositionProps = {
|
|
118
|
+
top: number;
|
|
119
|
+
left: number;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
type SizeProps = {
|
|
123
|
+
width: number;
|
|
124
|
+
height: number;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// In your editor, hovering over `Props` will show a flattened object with all the properties.
|
|
128
|
+
type Props = Simplify<PositionProps & SizeProps>;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Sometimes it is desired to pass a value as a function argument that has a different type. At first inspection it may seem assignable, and then you discover it is not because the `value`'s type definition was defined as an interface. In the following example, `fn` requires an argument of type `Record<string, unknown>`. If the value is defined as a literal, then it is assignable. And if the `value` is defined as type using the `Simplify` utility the value is assignable. But if the `value` is defined as an interface, it is not assignable because the interface is not sealed and elsewhere a non-string property could be added to the interface.
|
|
132
|
+
|
|
133
|
+
If the type definition must be an interface (perhaps it was defined in a third-party npm package), then the `value` can be defined as `const value: Simplify<SomeInterface> = ...`. Then `value` will be assignable to the `fn` argument. Or the `value` can be cast as `Simplify<SomeInterface>` if you can't re-declare the `value`.
|
|
134
|
+
|
|
135
|
+
@example
|
|
136
|
+
```
|
|
137
|
+
import type {Simplify} from 'type-fest';
|
|
138
|
+
|
|
139
|
+
interface SomeInterface {
|
|
140
|
+
foo: number;
|
|
141
|
+
bar?: string;
|
|
142
|
+
baz: number | undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
type SomeType = {
|
|
146
|
+
foo: number;
|
|
147
|
+
bar?: string;
|
|
148
|
+
baz: number | undefined;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const literal = {foo: 123, bar: 'hello', baz: 456};
|
|
152
|
+
const someType: SomeType = literal;
|
|
153
|
+
const someInterface: SomeInterface = literal;
|
|
154
|
+
|
|
155
|
+
function fn(object: Record<string, unknown>): void {}
|
|
156
|
+
|
|
157
|
+
fn(literal); // Good: literal object type is sealed
|
|
158
|
+
fn(someType); // Good: type is sealed
|
|
159
|
+
fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened
|
|
160
|
+
fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface` into a `type`
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
@link https://github.com/microsoft/TypeScript/issues/15300
|
|
164
|
+
@see SimplifyDeep
|
|
165
|
+
@category Object
|
|
166
|
+
*/
|
|
167
|
+
type Simplify<T> = {[KeyType in keyof T]: T[KeyType]} & {};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
Returns a boolean for whether the given type is `any`.
|
|
171
|
+
|
|
172
|
+
@link https://stackoverflow.com/a/49928360/1490091
|
|
173
|
+
|
|
174
|
+
Useful in type utilities, such as disallowing `any`s to be passed to a function.
|
|
175
|
+
|
|
176
|
+
@example
|
|
177
|
+
```
|
|
178
|
+
import type {IsAny} from 'type-fest';
|
|
179
|
+
|
|
180
|
+
const typedObject = {a: 1, b: 2} as const;
|
|
181
|
+
const anyObject: any = {a: 1, b: 2};
|
|
182
|
+
|
|
183
|
+
function get<O extends (IsAny<O> extends true ? {} : Record<string, number>), K extends keyof O = keyof O>(obj: O, key: K) {
|
|
184
|
+
return obj[key];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const typedA = get(typedObject, 'a');
|
|
188
|
+
//=> 1
|
|
189
|
+
|
|
190
|
+
const anyA = get(anyObject, 'a');
|
|
191
|
+
//=> any
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
@category Type Guard
|
|
195
|
+
@category Utilities
|
|
196
|
+
*/
|
|
197
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
198
|
+
|
|
199
|
+
type Numeric = number | bigint;
|
|
200
|
+
|
|
201
|
+
type Zero = 0 | 0n;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
Matches the hidden `Infinity` type.
|
|
205
|
+
|
|
206
|
+
Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/32277) if you want to have this type as a built-in in TypeScript.
|
|
207
|
+
|
|
208
|
+
@see NegativeInfinity
|
|
209
|
+
|
|
210
|
+
@category Numeric
|
|
211
|
+
*/
|
|
212
|
+
// See https://github.com/microsoft/TypeScript/issues/31752
|
|
213
|
+
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
|
|
214
|
+
type PositiveInfinity = 1e999;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
Matches the hidden `-Infinity` type.
|
|
218
|
+
|
|
219
|
+
Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/32277) if you want to have this type as a built-in in TypeScript.
|
|
220
|
+
|
|
221
|
+
@see PositiveInfinity
|
|
222
|
+
|
|
223
|
+
@category Numeric
|
|
224
|
+
*/
|
|
225
|
+
// See https://github.com/microsoft/TypeScript/issues/31752
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
|
|
227
|
+
type NegativeInfinity = -1e999;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
A negative `number`/`bigint` (`-β < x < 0`)
|
|
231
|
+
|
|
232
|
+
Use-case: Validating and documenting parameters.
|
|
233
|
+
|
|
234
|
+
@see NegativeInteger
|
|
235
|
+
@see NonNegative
|
|
236
|
+
|
|
237
|
+
@category Numeric
|
|
238
|
+
*/
|
|
239
|
+
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
Returns a boolean for whether the given number is a negative number.
|
|
243
|
+
|
|
244
|
+
@see Negative
|
|
245
|
+
|
|
246
|
+
@example
|
|
247
|
+
```
|
|
248
|
+
import type {IsNegative} from 'type-fest';
|
|
249
|
+
|
|
250
|
+
type ShouldBeFalse = IsNegative<1>;
|
|
251
|
+
type ShouldBeTrue = IsNegative<-1>;
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
@category Numeric
|
|
255
|
+
*/
|
|
256
|
+
type IsNegative<T extends Numeric> = T extends Negative<T> ? true : false;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
Returns a boolean for whether two given types are both true.
|
|
260
|
+
|
|
261
|
+
Use-case: Constructing complex conditional types where multiple conditions must be satisfied.
|
|
262
|
+
|
|
263
|
+
@example
|
|
264
|
+
```
|
|
265
|
+
import type {And} from 'type-fest';
|
|
266
|
+
|
|
267
|
+
And<true, true>;
|
|
268
|
+
//=> true
|
|
269
|
+
|
|
270
|
+
And<true, false>;
|
|
271
|
+
//=> false
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
@see {@link Or}
|
|
275
|
+
*/
|
|
276
|
+
type And<A extends boolean, B extends boolean> = [A, B][number] extends true
|
|
277
|
+
? true
|
|
278
|
+
: true extends [IsEqual<A, false>, IsEqual<B, false>][number]
|
|
279
|
+
? false
|
|
280
|
+
: never;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
Returns a boolean for whether either of two given types are true.
|
|
284
|
+
|
|
285
|
+
Use-case: Constructing complex conditional types where multiple conditions must be satisfied.
|
|
286
|
+
|
|
287
|
+
@example
|
|
288
|
+
```
|
|
289
|
+
import type {Or} from 'type-fest';
|
|
290
|
+
|
|
291
|
+
Or<true, false>;
|
|
292
|
+
//=> true
|
|
293
|
+
|
|
294
|
+
Or<false, false>;
|
|
295
|
+
//=> false
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
@see {@link And}
|
|
299
|
+
*/
|
|
300
|
+
type Or<A extends boolean, B extends boolean> = [A, B][number] extends false
|
|
301
|
+
? false
|
|
302
|
+
: true extends [IsEqual<A, true>, IsEqual<B, true>][number]
|
|
303
|
+
? true
|
|
304
|
+
: never;
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
Returns a boolean for whether a given number is greater than another number.
|
|
308
|
+
|
|
309
|
+
@example
|
|
310
|
+
```
|
|
311
|
+
import type {GreaterThan} from 'type-fest';
|
|
312
|
+
|
|
313
|
+
GreaterThan<1, -5>;
|
|
314
|
+
//=> true
|
|
315
|
+
|
|
316
|
+
GreaterThan<1, 1>;
|
|
317
|
+
//=> false
|
|
318
|
+
|
|
319
|
+
GreaterThan<1, 5>;
|
|
320
|
+
//=> false
|
|
321
|
+
```
|
|
322
|
+
*/
|
|
323
|
+
type GreaterThan<A extends number, B extends number> = number extends A | B
|
|
324
|
+
? never
|
|
325
|
+
: [
|
|
326
|
+
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
|
|
327
|
+
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
|
|
328
|
+
] extends infer R extends [boolean, boolean, boolean, boolean]
|
|
329
|
+
? Or<
|
|
330
|
+
And<IsEqual<R[0], true>, IsEqual<R[2], false>>,
|
|
331
|
+
And<IsEqual<R[3], true>, IsEqual<R[1], false>>
|
|
332
|
+
> extends true
|
|
333
|
+
? true
|
|
334
|
+
: Or<
|
|
335
|
+
And<IsEqual<R[1], true>, IsEqual<R[3], false>>,
|
|
336
|
+
And<IsEqual<R[2], true>, IsEqual<R[0], false>>
|
|
337
|
+
> extends true
|
|
338
|
+
? false
|
|
339
|
+
: true extends R[number]
|
|
340
|
+
? false
|
|
341
|
+
: [IsNegative<A>, IsNegative<B>] extends infer R extends [boolean, boolean]
|
|
342
|
+
? [true, false] extends R
|
|
343
|
+
? false
|
|
344
|
+
: [false, true] extends R
|
|
345
|
+
? true
|
|
346
|
+
: [false, false] extends R
|
|
347
|
+
? PositiveNumericStringGt<`${A}`, `${B}`>
|
|
348
|
+
: PositiveNumericStringGt<`${NumberAbsolute<B>}`, `${NumberAbsolute<A>}`>
|
|
349
|
+
: never
|
|
350
|
+
: never;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
Returns a boolean for whether a given number is greater than or equal to another number.
|
|
354
|
+
|
|
355
|
+
@example
|
|
356
|
+
```
|
|
357
|
+
import type {GreaterThanOrEqual} from 'type-fest';
|
|
358
|
+
|
|
359
|
+
GreaterThanOrEqual<1, -5>;
|
|
360
|
+
//=> true
|
|
361
|
+
|
|
362
|
+
GreaterThanOrEqual<1, 1>;
|
|
363
|
+
//=> true
|
|
364
|
+
|
|
365
|
+
GreaterThanOrEqual<1, 5>;
|
|
366
|
+
//=> false
|
|
367
|
+
```
|
|
368
|
+
*/
|
|
369
|
+
type GreaterThanOrEqual<A extends number, B extends number> = number extends A | B
|
|
370
|
+
? never
|
|
371
|
+
: A extends B ? true : GreaterThan<A, B>;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
Returns a boolean for whether a given number is less than another number.
|
|
375
|
+
|
|
376
|
+
@example
|
|
377
|
+
```
|
|
378
|
+
import type {LessThan} from 'type-fest';
|
|
379
|
+
|
|
380
|
+
LessThan<1, -5>;
|
|
381
|
+
//=> false
|
|
382
|
+
|
|
383
|
+
LessThan<1, 1>;
|
|
384
|
+
//=> false
|
|
385
|
+
|
|
386
|
+
LessThan<1, 5>;
|
|
387
|
+
//=> true
|
|
388
|
+
```
|
|
389
|
+
*/
|
|
390
|
+
type LessThan<A extends number, B extends number> = number extends A | B
|
|
391
|
+
? never
|
|
392
|
+
: GreaterThanOrEqual<A, B> extends true ? false : true;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
Returns a boolean for whether the given type is `never`.
|
|
396
|
+
|
|
397
|
+
@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919
|
|
398
|
+
@link https://stackoverflow.com/a/53984913/10292952
|
|
399
|
+
@link https://www.zhenghao.io/posts/ts-never
|
|
400
|
+
|
|
401
|
+
Useful in type utilities, such as checking if something does not occur.
|
|
402
|
+
|
|
403
|
+
@example
|
|
404
|
+
```
|
|
405
|
+
import type {IsNever, And} from 'type-fest';
|
|
406
|
+
|
|
407
|
+
// https://github.com/andnp/SimplyTyped/blob/master/src/types/strings.ts
|
|
408
|
+
type AreStringsEqual<A extends string, B extends string> =
|
|
409
|
+
And<
|
|
410
|
+
IsNever<Exclude<A, B>> extends true ? true : false,
|
|
411
|
+
IsNever<Exclude<B, A>> extends true ? true : false
|
|
412
|
+
>;
|
|
413
|
+
|
|
414
|
+
type EndIfEqual<I extends string, O extends string> =
|
|
415
|
+
AreStringsEqual<I, O> extends true
|
|
416
|
+
? never
|
|
417
|
+
: void;
|
|
418
|
+
|
|
419
|
+
function endIfEqual<I extends string, O extends string>(input: I, output: O): EndIfEqual<I, O> {
|
|
420
|
+
if (input === output) {
|
|
421
|
+
process.exit(0);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
endIfEqual('abc', 'abc');
|
|
426
|
+
//=> never
|
|
427
|
+
|
|
428
|
+
endIfEqual('abc', '123');
|
|
429
|
+
//=> void
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
@category Type Guard
|
|
433
|
+
@category Utilities
|
|
434
|
+
*/
|
|
435
|
+
type IsNever<T> = [T] extends [never] ? true : false;
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
Infer the length of the given tuple `<T>`.
|
|
439
|
+
|
|
440
|
+
Returns `never` if the given type is an non-fixed-length array like `Array<string>`.
|
|
441
|
+
|
|
442
|
+
@example
|
|
443
|
+
```
|
|
444
|
+
type Tuple = TupleLength<[string, number, boolean]>;
|
|
445
|
+
//=> 3
|
|
446
|
+
|
|
447
|
+
type Array = TupleLength<string[]>;
|
|
448
|
+
//=> never
|
|
449
|
+
|
|
450
|
+
// Supports union types.
|
|
451
|
+
type Union = TupleLength<[] | [1, 2, 3] | Array<number>>;
|
|
452
|
+
//=> 1 | 3
|
|
453
|
+
```
|
|
454
|
+
*/
|
|
455
|
+
type TupleLength<T extends UnknownArray> =
|
|
456
|
+
// `extends unknown` is used to convert `T` (if `T` is a union type) to
|
|
457
|
+
// a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types))
|
|
458
|
+
T extends unknown
|
|
459
|
+
? number extends T['length']
|
|
460
|
+
? never // Return never if the given type is an non-flexed-length array like `Array<string>`
|
|
461
|
+
: T['length']
|
|
462
|
+
: never; // Should never happen
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
Create a tuple type of the given length `<L>` and fill it with the given type `<Fill>`.
|
|
466
|
+
|
|
467
|
+
If `<Fill>` is not provided, it will default to `unknown`.
|
|
468
|
+
|
|
469
|
+
@link https://itnext.io/implementing-arithmetic-within-typescripts-type-system-a1ef140a6f6f
|
|
470
|
+
*/
|
|
471
|
+
type BuildTuple<L extends number, Fill = unknown, T extends readonly unknown[] = []> = T['length'] extends L
|
|
472
|
+
? T
|
|
473
|
+
: BuildTuple<L, Fill, [...T, Fill]>;
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
Create an object type with the given key `<Key>` and value `<Value>`.
|
|
477
|
+
|
|
478
|
+
It will copy the prefix and optional status of the same key from the given object `CopiedFrom` into the result.
|
|
479
|
+
|
|
480
|
+
@example
|
|
481
|
+
```
|
|
482
|
+
type A = BuildObject<'a', string>;
|
|
483
|
+
//=> {a: string}
|
|
484
|
+
|
|
485
|
+
// Copy `readonly` and `?` from the key `a` of `{readonly a?: any}`
|
|
486
|
+
type B = BuildObject<'a', string, {readonly a?: any}>;
|
|
487
|
+
//=> {readonly a?: string}
|
|
488
|
+
```
|
|
489
|
+
*/
|
|
490
|
+
type BuildObject<Key extends PropertyKey, Value, CopiedFrom extends object = {}> =
|
|
491
|
+
Key extends keyof CopiedFrom
|
|
492
|
+
? Pick<{[_ in keyof CopiedFrom]: Value}, Key>
|
|
493
|
+
: Key extends `${infer NumberKey extends number}`
|
|
494
|
+
? NumberKey extends keyof CopiedFrom
|
|
495
|
+
? Pick<{[_ in keyof CopiedFrom]: Value}, NumberKey>
|
|
496
|
+
: {[_ in Key]: Value}
|
|
497
|
+
: {[_ in Key]: Value};
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
Return a string representation of the given string or number.
|
|
501
|
+
|
|
502
|
+
Note: This type is not the return type of the `.toString()` function.
|
|
503
|
+
*/
|
|
504
|
+
type ToString<T> = T extends string | number ? `${T}` : never;
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
Matches any primitive, `void`, `Date`, or `RegExp` value.
|
|
508
|
+
*/
|
|
509
|
+
type BuiltIns = Primitive | void | Date | RegExp;
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
Matches non-recursive types.
|
|
513
|
+
*/
|
|
514
|
+
type NonRecursiveType = BuiltIns | Function | (new (...arguments_: any[]) => unknown);
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
Converts a numeric string to a number.
|
|
518
|
+
|
|
519
|
+
@example
|
|
520
|
+
```
|
|
521
|
+
type PositiveInt = StringToNumber<'1234'>;
|
|
522
|
+
//=> 1234
|
|
523
|
+
|
|
524
|
+
type NegativeInt = StringToNumber<'-1234'>;
|
|
525
|
+
//=> -1234
|
|
526
|
+
|
|
527
|
+
type PositiveFloat = StringToNumber<'1234.56'>;
|
|
528
|
+
//=> 1234.56
|
|
529
|
+
|
|
530
|
+
type NegativeFloat = StringToNumber<'-1234.56'>;
|
|
531
|
+
//=> -1234.56
|
|
532
|
+
|
|
533
|
+
type PositiveInfinity = StringToNumber<'Infinity'>;
|
|
534
|
+
//=> Infinity
|
|
535
|
+
|
|
536
|
+
type NegativeInfinity = StringToNumber<'-Infinity'>;
|
|
537
|
+
//=> -Infinity
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
@category String
|
|
541
|
+
@category Numeric
|
|
542
|
+
@category Template literal
|
|
543
|
+
*/
|
|
544
|
+
type StringToNumber<S extends string> = S extends `${infer N extends number}`
|
|
545
|
+
? N
|
|
546
|
+
: S extends 'Infinity'
|
|
547
|
+
? PositiveInfinity
|
|
548
|
+
: S extends '-Infinity'
|
|
549
|
+
? NegativeInfinity
|
|
550
|
+
: never;
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
Returns the length of the given string.
|
|
554
|
+
|
|
555
|
+
@example
|
|
556
|
+
```
|
|
557
|
+
StringLength<'abcde'>;
|
|
558
|
+
//=> 5
|
|
559
|
+
|
|
560
|
+
StringLength<string>;
|
|
561
|
+
//=> never
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
@category String
|
|
565
|
+
@category Template literal
|
|
566
|
+
*/
|
|
567
|
+
type StringLength<S extends string> = string extends S
|
|
568
|
+
? never
|
|
569
|
+
: StringToArray<S>['length'];
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
Returns an array of the characters of the string.
|
|
573
|
+
|
|
574
|
+
@example
|
|
575
|
+
```
|
|
576
|
+
StringToArray<'abcde'>;
|
|
577
|
+
//=> ['a', 'b', 'c', 'd', 'e']
|
|
578
|
+
|
|
579
|
+
StringToArray<string>;
|
|
580
|
+
//=> never
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
@category String
|
|
584
|
+
*/
|
|
585
|
+
type StringToArray<S extends string, Result extends string[] = []> = string extends S
|
|
586
|
+
? never
|
|
587
|
+
: S extends `${infer F}${infer R}`
|
|
588
|
+
? StringToArray<R, [...Result, F]>
|
|
589
|
+
: Result;
|
|
590
|
+
|
|
591
|
+
type StringDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
Extract the object field type if T is an object and K is a key of T, return `never` otherwise.
|
|
595
|
+
|
|
596
|
+
It creates a type-safe way to access the member type of `unknown` type.
|
|
597
|
+
*/
|
|
598
|
+
type ObjectValue<T, K> =
|
|
599
|
+
K extends keyof T
|
|
600
|
+
? T[K]
|
|
601
|
+
: ToString<K> extends keyof T
|
|
602
|
+
? T[ToString<K>]
|
|
603
|
+
: K extends `${infer NumberK extends number}`
|
|
604
|
+
? NumberK extends keyof T
|
|
605
|
+
? T[NumberK]
|
|
606
|
+
: never
|
|
607
|
+
: never;
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
Returns the maximum value from a tuple of integers.
|
|
611
|
+
|
|
612
|
+
Note:
|
|
613
|
+
- Float numbers are not supported.
|
|
614
|
+
|
|
615
|
+
@example
|
|
616
|
+
```
|
|
617
|
+
ArrayMax<[1, 2, 5, 3]>;
|
|
618
|
+
//=> 5
|
|
619
|
+
|
|
620
|
+
ArrayMax<[1, 2, 5, 3, 99, -1]>;
|
|
621
|
+
//=> 99
|
|
622
|
+
```
|
|
623
|
+
*/
|
|
624
|
+
type ArrayMax<A extends number[], Result extends number = NegativeInfinity> = number extends A[number]
|
|
625
|
+
? never :
|
|
626
|
+
A extends [infer F extends number, ...infer R extends number[]]
|
|
627
|
+
? GreaterThan<F, Result> extends true
|
|
628
|
+
? ArrayMax<R, F>
|
|
629
|
+
: ArrayMax<R, Result>
|
|
630
|
+
: Result;
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
Returns the minimum value from a tuple of integers.
|
|
634
|
+
|
|
635
|
+
Note:
|
|
636
|
+
- Float numbers are not supported.
|
|
637
|
+
|
|
638
|
+
@example
|
|
639
|
+
```
|
|
640
|
+
ArrayMin<[1, 2, 5, 3]>;
|
|
641
|
+
//=> 1
|
|
642
|
+
|
|
643
|
+
ArrayMin<[1, 2, 5, 3, -5]>;
|
|
644
|
+
//=> -5
|
|
645
|
+
```
|
|
646
|
+
*/
|
|
647
|
+
type ArrayMin<A extends number[], Result extends number = PositiveInfinity> = number extends A[number]
|
|
648
|
+
? never
|
|
649
|
+
: A extends [infer F extends number, ...infer R extends number[]]
|
|
650
|
+
? LessThan<F, Result> extends true
|
|
651
|
+
? ArrayMin<R, F>
|
|
652
|
+
: ArrayMin<R, Result>
|
|
653
|
+
: Result;
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
Returns the absolute value of a given value.
|
|
657
|
+
|
|
658
|
+
@example
|
|
659
|
+
```
|
|
660
|
+
NumberAbsolute<-1>;
|
|
661
|
+
//=> 1
|
|
662
|
+
|
|
663
|
+
NumberAbsolute<1>;
|
|
664
|
+
//=> 1
|
|
665
|
+
|
|
666
|
+
NumberAbsolute<NegativeInfinity>
|
|
667
|
+
//=> PositiveInfinity
|
|
668
|
+
```
|
|
669
|
+
*/
|
|
670
|
+
type NumberAbsolute<N extends number> = `${N}` extends `-${infer StringPositiveN}` ? StringToNumber<StringPositiveN> : N;
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
Returns a boolean for whether `A` represents a number greater than `B`, where `A` and `B` are both numeric strings and have the same length.
|
|
674
|
+
|
|
675
|
+
@example
|
|
676
|
+
```
|
|
677
|
+
SameLengthPositiveNumericStringGt<'50', '10'>;
|
|
678
|
+
//=> true
|
|
679
|
+
|
|
680
|
+
SameLengthPositiveNumericStringGt<'10', '10'>;
|
|
681
|
+
//=> false
|
|
682
|
+
```
|
|
683
|
+
*/
|
|
684
|
+
type SameLengthPositiveNumericStringGt<A extends string, B extends string> = A extends `${infer FirstA}${infer RestA}`
|
|
685
|
+
? B extends `${infer FirstB}${infer RestB}`
|
|
686
|
+
? FirstA extends FirstB
|
|
687
|
+
? SameLengthPositiveNumericStringGt<RestA, RestB>
|
|
688
|
+
: PositiveNumericCharacterGt<FirstA, FirstB>
|
|
689
|
+
: never
|
|
690
|
+
: false;
|
|
691
|
+
|
|
692
|
+
type NumericString = '0123456789';
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
Returns a boolean for whether `A` is greater than `B`, where `A` and `B` are both positive numeric strings.
|
|
696
|
+
|
|
697
|
+
@example
|
|
698
|
+
```
|
|
699
|
+
PositiveNumericStringGt<'500', '1'>;
|
|
700
|
+
//=> true
|
|
701
|
+
|
|
702
|
+
PositiveNumericStringGt<'1', '1'>;
|
|
703
|
+
//=> false
|
|
704
|
+
|
|
705
|
+
PositiveNumericStringGt<'1', '500'>;
|
|
706
|
+
//=> false
|
|
707
|
+
```
|
|
708
|
+
*/
|
|
709
|
+
type PositiveNumericStringGt<A extends string, B extends string> = A extends B
|
|
710
|
+
? false
|
|
711
|
+
: [BuildTuple<StringLength<A>, 0>, BuildTuple<StringLength<B>, 0>] extends infer R extends [readonly unknown[], readonly unknown[]]
|
|
712
|
+
? R[0] extends [...R[1], ...infer Remain extends readonly unknown[]]
|
|
713
|
+
? 0 extends Remain['length']
|
|
714
|
+
? SameLengthPositiveNumericStringGt<A, B>
|
|
715
|
+
: true
|
|
716
|
+
: false
|
|
717
|
+
: never;
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
Returns a boolean for whether `A` represents a number greater than `B`, where `A` and `B` are both positive numeric characters.
|
|
721
|
+
|
|
722
|
+
@example
|
|
723
|
+
```
|
|
724
|
+
PositiveNumericCharacterGt<'5', '1'>;
|
|
725
|
+
//=> true
|
|
726
|
+
|
|
727
|
+
PositiveNumericCharacterGt<'1', '1'>;
|
|
728
|
+
//=> false
|
|
729
|
+
```
|
|
730
|
+
*/
|
|
731
|
+
type PositiveNumericCharacterGt<A extends string, B extends string> = NumericString extends `${infer HeadA}${A}${infer TailA}`
|
|
732
|
+
? NumericString extends `${infer HeadB}${B}${infer TailB}`
|
|
733
|
+
? HeadA extends `${HeadB}${infer _}${infer __}`
|
|
734
|
+
? true
|
|
735
|
+
: false
|
|
736
|
+
: never
|
|
737
|
+
: never;
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
Returns the static, fixed-length portion of the given array, excluding variable-length parts.
|
|
741
|
+
|
|
742
|
+
@example
|
|
743
|
+
```
|
|
744
|
+
type A = [string, number, boolean, ...string[]];
|
|
745
|
+
type B = StaticPartOfArray<A>;
|
|
746
|
+
//=> [string, number, boolean]
|
|
747
|
+
```
|
|
748
|
+
*/
|
|
749
|
+
type StaticPartOfArray<T extends UnknownArray, Result extends UnknownArray = []> =
|
|
750
|
+
T extends unknown
|
|
751
|
+
? number extends T['length'] ?
|
|
752
|
+
T extends readonly [infer U, ...infer V]
|
|
753
|
+
? StaticPartOfArray<V, [...Result, U]>
|
|
754
|
+
: Result
|
|
755
|
+
: T
|
|
756
|
+
: never; // Should never happen
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
Returns the variable, non-fixed-length portion of the given array, excluding static-length parts.
|
|
760
|
+
|
|
761
|
+
@example
|
|
762
|
+
```
|
|
763
|
+
type A = [string, number, boolean, ...string[]];
|
|
764
|
+
type B = VariablePartOfArray<A>;
|
|
765
|
+
//=> string[]
|
|
766
|
+
```
|
|
767
|
+
*/
|
|
768
|
+
type VariablePartOfArray<T extends UnknownArray> =
|
|
769
|
+
T extends unknown
|
|
770
|
+
? T extends readonly [...StaticPartOfArray<T>, ...infer U]
|
|
771
|
+
? U
|
|
772
|
+
: []
|
|
773
|
+
: never; // Should never happen
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
Returns the minimum number in the given union of numbers.
|
|
777
|
+
|
|
778
|
+
Note: Just supports numbers from 0 to 999.
|
|
779
|
+
|
|
780
|
+
@example
|
|
781
|
+
```
|
|
782
|
+
type A = UnionMin<3 | 1 | 2>;
|
|
783
|
+
//=> 1
|
|
784
|
+
```
|
|
785
|
+
*/
|
|
786
|
+
type UnionMin<N extends number> = InternalUnionMin<N>;
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
The actual implementation of `UnionMin`. It's private because it has some arguments that don't need to be exposed.
|
|
790
|
+
*/
|
|
791
|
+
type InternalUnionMin<N extends number, T extends UnknownArray = []> =
|
|
792
|
+
T['length'] extends N
|
|
793
|
+
? T['length']
|
|
794
|
+
: InternalUnionMin<N, [...T, unknown]>;
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
Returns the maximum number in the given union of numbers.
|
|
798
|
+
|
|
799
|
+
Note: Just supports numbers from 0 to 999.
|
|
800
|
+
|
|
801
|
+
@example
|
|
802
|
+
```
|
|
803
|
+
type A = UnionMax<1 | 3 | 2>;
|
|
804
|
+
//=> 3
|
|
805
|
+
```
|
|
806
|
+
*/
|
|
807
|
+
type UnionMax<N extends number> = InternalUnionMax<N>;
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
The actual implementation of `UnionMax`. It's private because it has some arguments that don't need to be exposed.
|
|
811
|
+
*/
|
|
812
|
+
type InternalUnionMax<N extends number, T extends UnknownArray = []> =
|
|
813
|
+
IsNever<N> extends true
|
|
814
|
+
? T['length']
|
|
815
|
+
: T['length'] extends N
|
|
816
|
+
? InternalUnionMax<Exclude<N, T['length']>, T>
|
|
817
|
+
: InternalUnionMax<N, [...T, unknown]>;
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
Returns a boolean for whether the given type is a union type.
|
|
821
|
+
|
|
822
|
+
@example
|
|
823
|
+
```
|
|
824
|
+
type A = IsUnion<string | number>;
|
|
825
|
+
//=> true
|
|
826
|
+
|
|
827
|
+
type B = IsUnion<string>;
|
|
828
|
+
//=> false
|
|
829
|
+
```
|
|
830
|
+
*/
|
|
831
|
+
type IsUnion<T> = InternalIsUnion<T>;
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
The actual implementation of `IsUnion`.
|
|
835
|
+
*/
|
|
836
|
+
type InternalIsUnion<T, U = T> =
|
|
837
|
+
(
|
|
838
|
+
// @link https://ghaiklor.github.io/type-challenges-solutions/en/medium-isunion.html
|
|
839
|
+
IsNever<T> extends true
|
|
840
|
+
? false
|
|
841
|
+
: T extends any
|
|
842
|
+
? [U] extends [T]
|
|
843
|
+
? false
|
|
844
|
+
: true
|
|
845
|
+
: never
|
|
846
|
+
) extends infer Result
|
|
847
|
+
// In some cases `Result` will return `false | true` which is `boolean`,
|
|
848
|
+
// that means `T` has at least two types and it's a union type,
|
|
849
|
+
// so we will return `true` instead of `boolean`.
|
|
850
|
+
? boolean extends Result ? true
|
|
851
|
+
: Result
|
|
852
|
+
: never; // Should never happen
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
Set the given array to readonly if `IsReadonly` is `true`, otherwise set the given array to normal, then return the result.
|
|
856
|
+
|
|
857
|
+
@example
|
|
858
|
+
```
|
|
859
|
+
type ReadonlyArray = readonly string[];
|
|
860
|
+
type NormalArray = string[];
|
|
861
|
+
|
|
862
|
+
type ReadonlyResult = SetArrayAccess<NormalArray, true>;
|
|
863
|
+
//=> readonly string[]
|
|
864
|
+
|
|
865
|
+
type NormalResult = SetArrayAccess<ReadonlyArray, false>;
|
|
866
|
+
//=> string[]
|
|
867
|
+
```
|
|
868
|
+
*/
|
|
869
|
+
type SetArrayAccess<T extends UnknownArray, IsReadonly extends boolean> =
|
|
870
|
+
T extends readonly [...infer U] ?
|
|
871
|
+
IsReadonly extends true
|
|
872
|
+
? readonly [...U]
|
|
873
|
+
: [...U]
|
|
874
|
+
: T;
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
Returns whether the given array `T` is readonly.
|
|
878
|
+
*/
|
|
879
|
+
type IsArrayReadonly<T extends UnknownArray> = T extends unknown[] ? false : true;
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
Get the exact version of the given `Key` in the given object `T`.
|
|
883
|
+
|
|
884
|
+
Use-case: You known that a number key (e.g. 10) is in an object, but you don't know how it is defined in the object, as a string or as a number (e.g. 10 or '10'). You can use this type to get the exact version of the key. See the example.
|
|
885
|
+
|
|
886
|
+
@example
|
|
887
|
+
```
|
|
888
|
+
type Object = {
|
|
889
|
+
0: number;
|
|
890
|
+
'1': string;
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
type Key1 = ExactKey<Object, '0'>;
|
|
894
|
+
//=> 0
|
|
895
|
+
type Key2 = ExactKey<Object, 0>;
|
|
896
|
+
//=> 0
|
|
897
|
+
|
|
898
|
+
type Key3 = ExactKey<Object, '1'>;
|
|
899
|
+
//=> '1'
|
|
900
|
+
type Key4 = ExactKey<Object, 1>;
|
|
901
|
+
//=> '1'
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
@category Object
|
|
905
|
+
*/
|
|
906
|
+
type ExactKey<T extends object, Key extends PropertyKey> =
|
|
907
|
+
Key extends keyof T
|
|
908
|
+
? Key
|
|
909
|
+
: ToString<Key> extends keyof T
|
|
910
|
+
? ToString<Key>
|
|
911
|
+
: Key extends `${infer NumberKey extends number}`
|
|
912
|
+
? NumberKey extends keyof T
|
|
913
|
+
? NumberKey
|
|
914
|
+
: never
|
|
915
|
+
: never;
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
Deeply simplifies an object type.
|
|
919
|
+
|
|
920
|
+
You can exclude certain types from being simplified by providing them in the second generic `ExcludeType`.
|
|
921
|
+
|
|
922
|
+
Useful to flatten the type output to improve type hints shown in editors.
|
|
923
|
+
|
|
924
|
+
@example
|
|
925
|
+
```
|
|
926
|
+
import type {SimplifyDeep} from 'type-fest';
|
|
927
|
+
|
|
928
|
+
type PositionX = {
|
|
929
|
+
left: number;
|
|
930
|
+
right: number;
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
type PositionY = {
|
|
934
|
+
top: number;
|
|
935
|
+
bottom: number;
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
type Properties1 = {
|
|
939
|
+
height: number;
|
|
940
|
+
position: PositionY;
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
type Properties2 = {
|
|
944
|
+
width: number;
|
|
945
|
+
position: PositionX;
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
type Properties = Properties1 & Properties2;
|
|
949
|
+
// In your editor, hovering over `Props` will show the following:
|
|
950
|
+
//
|
|
951
|
+
// type Properties = Properties1 & Properties2;
|
|
952
|
+
|
|
953
|
+
type SimplifyDeepProperties = SimplifyDeep<Properties1 & Properties2>;
|
|
954
|
+
// But if wrapped in SimplifyDeep, hovering over `SimplifyDeepProperties` will show a flattened object with all the properties:
|
|
955
|
+
//
|
|
956
|
+
// SimplifyDeepProperties = {
|
|
957
|
+
// height: number;
|
|
958
|
+
// width: number;
|
|
959
|
+
// position: {
|
|
960
|
+
// top: number;
|
|
961
|
+
// bottom: number;
|
|
962
|
+
// left: number;
|
|
963
|
+
// right: number;
|
|
964
|
+
// };
|
|
965
|
+
// };
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
@example
|
|
969
|
+
```
|
|
970
|
+
import type {SimplifyDeep} from 'type-fest';
|
|
971
|
+
|
|
972
|
+
// A complex type that you don't want or need to simplify
|
|
973
|
+
type ComplexType = {
|
|
974
|
+
a: string;
|
|
975
|
+
b: 'b';
|
|
976
|
+
c: number;
|
|
977
|
+
...
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
type PositionX = {
|
|
981
|
+
left: number;
|
|
982
|
+
right: number;
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
type PositionY = {
|
|
986
|
+
top: number;
|
|
987
|
+
bottom: number;
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
// You want to simplify all other types
|
|
991
|
+
type Properties1 = {
|
|
992
|
+
height: number;
|
|
993
|
+
position: PositionY;
|
|
994
|
+
foo: ComplexType;
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
type Properties2 = {
|
|
998
|
+
width: number;
|
|
999
|
+
position: PositionX;
|
|
1000
|
+
foo: ComplexType;
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
type SimplifyDeepProperties = SimplifyDeep<Properties1 & Properties2, ComplexType>;
|
|
1004
|
+
// If wrapped in `SimplifyDeep` and set `ComplexType` to exclude, hovering over `SimplifyDeepProperties` will
|
|
1005
|
+
// show a flattened object with all the properties except `ComplexType`:
|
|
1006
|
+
//
|
|
1007
|
+
// SimplifyDeepProperties = {
|
|
1008
|
+
// height: number;
|
|
1009
|
+
// width: number;
|
|
1010
|
+
// position: {
|
|
1011
|
+
// top: number;
|
|
1012
|
+
// bottom: number;
|
|
1013
|
+
// left: number;
|
|
1014
|
+
// right: number;
|
|
1015
|
+
// };
|
|
1016
|
+
// foo: ComplexType;
|
|
1017
|
+
// };
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
@see Simplify
|
|
1021
|
+
@category Object
|
|
1022
|
+
*/
|
|
1023
|
+
type SimplifyDeep<Type, ExcludeType = never> =
|
|
1024
|
+
ConditionalSimplifyDeep<
|
|
1025
|
+
Type,
|
|
1026
|
+
ExcludeType | NonRecursiveType | Set<unknown> | Map<unknown, unknown>,
|
|
1027
|
+
object
|
|
1028
|
+
>;
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
Returns the difference between two numbers.
|
|
1032
|
+
|
|
1033
|
+
Note:
|
|
1034
|
+
- A or B can only support `-999` ~ `999`.
|
|
1035
|
+
- If the result is negative, you can only get `number`.
|
|
1036
|
+
|
|
1037
|
+
@example
|
|
1038
|
+
```
|
|
1039
|
+
import type {Subtract} from 'type-fest';
|
|
1040
|
+
|
|
1041
|
+
Subtract<333, 222>;
|
|
1042
|
+
//=> 111
|
|
1043
|
+
|
|
1044
|
+
Subtract<111, -222>;
|
|
1045
|
+
//=> 333
|
|
1046
|
+
|
|
1047
|
+
Subtract<-111, 222>;
|
|
1048
|
+
//=> number
|
|
1049
|
+
|
|
1050
|
+
Subtract<PositiveInfinity, 9999>;
|
|
1051
|
+
//=> PositiveInfinity
|
|
1052
|
+
|
|
1053
|
+
Subtract<PositiveInfinity, PositiveInfinity>;
|
|
1054
|
+
//=> number
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
@category Numeric
|
|
1058
|
+
*/
|
|
1059
|
+
// TODO: Support big integer and negative number.
|
|
1060
|
+
type Subtract<A extends number, B extends number> = number extends A | B
|
|
1061
|
+
? number
|
|
1062
|
+
: [
|
|
1063
|
+
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
|
|
1064
|
+
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
|
|
1065
|
+
] extends infer R extends [boolean, boolean, boolean, boolean]
|
|
1066
|
+
? Or<
|
|
1067
|
+
And<IsEqual<R[0], true>, IsEqual<R[2], false>>,
|
|
1068
|
+
And<IsEqual<R[3], true>, IsEqual<R[1], false>>
|
|
1069
|
+
> extends true
|
|
1070
|
+
? PositiveInfinity
|
|
1071
|
+
: Or<
|
|
1072
|
+
And<IsEqual<R[1], true>, IsEqual<R[3], false>>,
|
|
1073
|
+
And<IsEqual<R[2], true>, IsEqual<R[0], false>>
|
|
1074
|
+
> extends true
|
|
1075
|
+
? NegativeInfinity
|
|
1076
|
+
: true extends R[number]
|
|
1077
|
+
? number
|
|
1078
|
+
: [IsNegative<A>, IsNegative<B>] extends infer R
|
|
1079
|
+
? [false, false] extends R
|
|
1080
|
+
? BuildTuple<A> extends infer R
|
|
1081
|
+
? R extends [...BuildTuple<B>, ...infer R]
|
|
1082
|
+
? R['length']
|
|
1083
|
+
: number
|
|
1084
|
+
: never
|
|
1085
|
+
: LessThan<A, B> extends true
|
|
1086
|
+
? number
|
|
1087
|
+
: [false, true] extends R
|
|
1088
|
+
? Sum<A, NumberAbsolute<B>>
|
|
1089
|
+
: Subtract<NumberAbsolute<B>, NumberAbsolute<A>>
|
|
1090
|
+
: never
|
|
1091
|
+
: never;
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
Returns the sum of two numbers.
|
|
1095
|
+
|
|
1096
|
+
Note:
|
|
1097
|
+
- A or B can only support `-999` ~ `999`.
|
|
1098
|
+
- A and B can only be small integers, less than 1000.
|
|
1099
|
+
- If the result is negative, you can only get `number`.
|
|
1100
|
+
|
|
1101
|
+
@example
|
|
1102
|
+
```
|
|
1103
|
+
import type {Sum} from 'type-fest';
|
|
1104
|
+
|
|
1105
|
+
Sum<111, 222>;
|
|
1106
|
+
//=> 333
|
|
1107
|
+
|
|
1108
|
+
Sum<-111, 222>;
|
|
1109
|
+
//=> 111
|
|
1110
|
+
|
|
1111
|
+
Sum<111, -222>;
|
|
1112
|
+
//=> number
|
|
1113
|
+
|
|
1114
|
+
Sum<PositiveInfinity, -9999>;
|
|
1115
|
+
//=> PositiveInfinity
|
|
1116
|
+
|
|
1117
|
+
Sum<PositiveInfinity, NegativeInfinity>;
|
|
1118
|
+
//=> number
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
@category Numeric
|
|
1122
|
+
*/
|
|
1123
|
+
// TODO: Support big integer and negative number.
|
|
1124
|
+
type Sum<A extends number, B extends number> = number extends A | B
|
|
1125
|
+
? number
|
|
1126
|
+
: [
|
|
1127
|
+
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
|
|
1128
|
+
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
|
|
1129
|
+
] extends infer R extends [boolean, boolean, boolean, boolean]
|
|
1130
|
+
? Or<
|
|
1131
|
+
And<IsEqual<R[0], true>, IsEqual<R[3], false>>,
|
|
1132
|
+
And<IsEqual<R[2], true>, IsEqual<R[1], false>>
|
|
1133
|
+
> extends true
|
|
1134
|
+
? PositiveInfinity
|
|
1135
|
+
: Or<
|
|
1136
|
+
And<IsEqual<R[1], true>, IsEqual<R[2], false>>,
|
|
1137
|
+
And<IsEqual<R[3], true>, IsEqual<R[0], false>>
|
|
1138
|
+
> extends true
|
|
1139
|
+
? NegativeInfinity
|
|
1140
|
+
: true extends R[number]
|
|
1141
|
+
? number
|
|
1142
|
+
: ([IsNegative<A>, IsNegative<B>] extends infer R
|
|
1143
|
+
? [false, false] extends R
|
|
1144
|
+
? [...BuildTuple<A>, ...BuildTuple<B>]['length']
|
|
1145
|
+
: [true, true] extends R
|
|
1146
|
+
? number
|
|
1147
|
+
: ArrayMax<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Max_
|
|
1148
|
+
? ArrayMin<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Min_ extends number
|
|
1149
|
+
? Max_ extends A | B
|
|
1150
|
+
? Subtract<Max_, Min_>
|
|
1151
|
+
: number
|
|
1152
|
+
: never
|
|
1153
|
+
: never
|
|
1154
|
+
: never) & number
|
|
1155
|
+
: never;
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
Generate a union of all possible paths to properties in the given object.
|
|
1159
|
+
|
|
1160
|
+
It also works with arrays.
|
|
1161
|
+
|
|
1162
|
+
Use-case: You want a type-safe way to access deeply nested properties in an object.
|
|
1163
|
+
|
|
1164
|
+
@example
|
|
1165
|
+
```
|
|
1166
|
+
import type {Paths} from 'type-fest';
|
|
1167
|
+
|
|
1168
|
+
type Project = {
|
|
1169
|
+
filename: string;
|
|
1170
|
+
listA: string[];
|
|
1171
|
+
listB: [{filename: string}];
|
|
1172
|
+
folder: {
|
|
1173
|
+
subfolder: {
|
|
1174
|
+
filename: string;
|
|
1175
|
+
};
|
|
1176
|
+
};
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
type ProjectPaths = Paths<Project>;
|
|
1180
|
+
//=> 'filename' | 'listA' | 'listB' | 'folder' | `listA.${number}` | 'listB.0' | 'listB.0.filename' | 'folder.subfolder' | 'folder.subfolder.filename'
|
|
1181
|
+
|
|
1182
|
+
declare function open<Path extends ProjectPaths>(path: Path): void;
|
|
1183
|
+
|
|
1184
|
+
open('filename'); // Pass
|
|
1185
|
+
open('folder.subfolder'); // Pass
|
|
1186
|
+
open('folder.subfolder.filename'); // Pass
|
|
1187
|
+
open('foo'); // TypeError
|
|
1188
|
+
|
|
1189
|
+
// Also works with arrays
|
|
1190
|
+
open('listA.1'); // Pass
|
|
1191
|
+
open('listB.0'); // Pass
|
|
1192
|
+
open('listB.1'); // TypeError. Because listB only has one element.
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
@category Object
|
|
1196
|
+
@category Array
|
|
1197
|
+
*/
|
|
1198
|
+
type Paths<T> = Paths_<T>;
|
|
1199
|
+
|
|
1200
|
+
type Paths_<T, Depth extends number = 0> =
|
|
1201
|
+
T extends NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
|
|
1202
|
+
? never
|
|
1203
|
+
: IsAny<T> extends true
|
|
1204
|
+
? never
|
|
1205
|
+
: T extends UnknownArray
|
|
1206
|
+
? number extends T['length']
|
|
1207
|
+
// We need to handle the fixed and non-fixed index part of the array separately.
|
|
1208
|
+
? InternalPaths<StaticPartOfArray<T>, Depth>
|
|
1209
|
+
| InternalPaths<Array<VariablePartOfArray<T>[number]>, Depth>
|
|
1210
|
+
: InternalPaths<T, Depth>
|
|
1211
|
+
: T extends object
|
|
1212
|
+
? InternalPaths<T, Depth>
|
|
1213
|
+
: never;
|
|
1214
|
+
|
|
1215
|
+
type InternalPaths<_T, Depth extends number = 0, T = Required<_T>> =
|
|
1216
|
+
T extends EmptyObject | readonly []
|
|
1217
|
+
? never
|
|
1218
|
+
: {
|
|
1219
|
+
[Key in keyof T]:
|
|
1220
|
+
Key extends string | number // Limit `Key` to string or number.
|
|
1221
|
+
// If `Key` is a number, return `Key | `${Key}``, because both `array[0]` and `array['0']` work.
|
|
1222
|
+
?
|
|
1223
|
+
| Key
|
|
1224
|
+
| ToString<Key>
|
|
1225
|
+
| (
|
|
1226
|
+
LessThan<Depth, 15> extends true // Limit the depth to prevent infinite recursion
|
|
1227
|
+
? IsNever<Paths_<T[Key], Sum<Depth, 1>>> extends false
|
|
1228
|
+
? `${Key}.${Paths_<T[Key], Sum<Depth, 1>>}`
|
|
1229
|
+
: never
|
|
1230
|
+
: never
|
|
1231
|
+
)
|
|
1232
|
+
: never
|
|
1233
|
+
}[keyof T & (T extends UnknownArray ? number : unknown)];
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
Convert a union type to an intersection type using [distributive conditional types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
|
|
1237
|
+
|
|
1238
|
+
Inspired by [this Stack Overflow answer](https://stackoverflow.com/a/50375286/2172153).
|
|
1239
|
+
|
|
1240
|
+
@example
|
|
1241
|
+
```
|
|
1242
|
+
import type {UnionToIntersection} from 'type-fest';
|
|
1243
|
+
|
|
1244
|
+
type Union = {the(): void} | {great(arg: string): void} | {escape: boolean};
|
|
1245
|
+
|
|
1246
|
+
type Intersection = UnionToIntersection<Union>;
|
|
1247
|
+
//=> {the(): void; great(arg: string): void; escape: boolean};
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
A more applicable example which could make its way into your library code follows.
|
|
1251
|
+
|
|
1252
|
+
@example
|
|
1253
|
+
```
|
|
1254
|
+
import type {UnionToIntersection} from 'type-fest';
|
|
1255
|
+
|
|
1256
|
+
class CommandOne {
|
|
1257
|
+
commands: {
|
|
1258
|
+
a1: () => undefined,
|
|
1259
|
+
b1: () => undefined,
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
class CommandTwo {
|
|
1264
|
+
commands: {
|
|
1265
|
+
a2: (argA: string) => undefined,
|
|
1266
|
+
b2: (argB: string) => undefined,
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
const union = [new CommandOne(), new CommandTwo()].map(instance => instance.commands);
|
|
1271
|
+
type Union = typeof union;
|
|
1272
|
+
//=> {a1(): void; b1(): void} | {a2(argA: string): void; b2(argB: string): void}
|
|
1273
|
+
|
|
1274
|
+
type Intersection = UnionToIntersection<Union>;
|
|
1275
|
+
//=> {a1(): void; b1(): void; a2(argA: string): void; b2(argB: string): void}
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
@category Type
|
|
1279
|
+
*/
|
|
1280
|
+
type UnionToIntersection<Union> = (
|
|
1281
|
+
// `extends unknown` is always going to be the case and is used to convert the
|
|
1282
|
+
// `Union` into a [distributive conditional
|
|
1283
|
+
// type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
|
|
1284
|
+
Union extends unknown
|
|
1285
|
+
// The union type is used as the only argument to a function since the union
|
|
1286
|
+
// of function arguments is an intersection.
|
|
1287
|
+
? (distributedUnion: Union) => void
|
|
1288
|
+
// This won't happen.
|
|
1289
|
+
: never
|
|
1290
|
+
// Infer the `Intersection` type since TypeScript represents the positional
|
|
1291
|
+
// arguments of unions of functions as an intersection of the union.
|
|
1292
|
+
) extends ((mergedIntersection: infer Intersection) => void)
|
|
1293
|
+
// The `& Union` is to allow indexing by the resulting type
|
|
1294
|
+
? Intersection & Union
|
|
1295
|
+
: never;
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
Pick properties from a deeply-nested object.
|
|
1299
|
+
|
|
1300
|
+
It supports recursing into arrays.
|
|
1301
|
+
|
|
1302
|
+
Use-case: Distill complex objects down to the components you need to target.
|
|
1303
|
+
|
|
1304
|
+
@example
|
|
1305
|
+
```
|
|
1306
|
+
import type {PickDeep, PartialDeep} from 'type-fest';
|
|
1307
|
+
|
|
1308
|
+
type Configuration = {
|
|
1309
|
+
userConfig: {
|
|
1310
|
+
name: string;
|
|
1311
|
+
age: number;
|
|
1312
|
+
address: [
|
|
1313
|
+
{
|
|
1314
|
+
city1: string;
|
|
1315
|
+
street1: string;
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
city2: string;
|
|
1319
|
+
street2: string;
|
|
1320
|
+
}
|
|
1321
|
+
]
|
|
1322
|
+
};
|
|
1323
|
+
otherConfig: any;
|
|
1324
|
+
};
|
|
1325
|
+
|
|
1326
|
+
type NameConfig = PickDeep<Configuration, 'userConfig.name'>;
|
|
1327
|
+
// type NameConfig = {
|
|
1328
|
+
// userConfig: {
|
|
1329
|
+
// name: string;
|
|
1330
|
+
// }
|
|
1331
|
+
// };
|
|
1332
|
+
|
|
1333
|
+
// Supports optional properties
|
|
1334
|
+
type User = PickDeep<PartialDeep<Configuration>, 'userConfig.name' | 'userConfig.age'>;
|
|
1335
|
+
// type User = {
|
|
1336
|
+
// userConfig?: {
|
|
1337
|
+
// name?: string;
|
|
1338
|
+
// age?: number;
|
|
1339
|
+
// };
|
|
1340
|
+
// };
|
|
1341
|
+
|
|
1342
|
+
// Supports array
|
|
1343
|
+
type AddressConfig = PickDeep<Configuration, 'userConfig.address.0'>;
|
|
1344
|
+
// type AddressConfig = {
|
|
1345
|
+
// userConfig: {
|
|
1346
|
+
// address: [{
|
|
1347
|
+
// city1: string;
|
|
1348
|
+
// street1: string;
|
|
1349
|
+
// }];
|
|
1350
|
+
// };
|
|
1351
|
+
// }
|
|
1352
|
+
|
|
1353
|
+
// Supports recurse into array
|
|
1354
|
+
type Street = PickDeep<Configuration, 'userConfig.address.1.street2'>;
|
|
1355
|
+
// type Street = {
|
|
1356
|
+
// userConfig: {
|
|
1357
|
+
// address: [
|
|
1358
|
+
// unknown,
|
|
1359
|
+
// {street2: string}
|
|
1360
|
+
// ];
|
|
1361
|
+
// };
|
|
1362
|
+
// }
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
@category Object
|
|
1366
|
+
@category Array
|
|
1367
|
+
*/
|
|
1368
|
+
type PickDeep<T, PathUnion extends Paths<T>> =
|
|
1369
|
+
T extends NonRecursiveType
|
|
1370
|
+
? never
|
|
1371
|
+
: T extends UnknownArray
|
|
1372
|
+
? UnionToIntersection<{
|
|
1373
|
+
[P in PathUnion]: InternalPickDeep<T, P>;
|
|
1374
|
+
}[PathUnion]
|
|
1375
|
+
>
|
|
1376
|
+
: T extends object
|
|
1377
|
+
? Simplify<UnionToIntersection<{
|
|
1378
|
+
[P in PathUnion]: InternalPickDeep<T, P>;
|
|
1379
|
+
}[PathUnion]>>
|
|
1380
|
+
: never;
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
Pick an object/array from the given object/array by one path.
|
|
1384
|
+
*/
|
|
1385
|
+
type InternalPickDeep<T, Path extends string | number> =
|
|
1386
|
+
T extends NonRecursiveType
|
|
1387
|
+
? never
|
|
1388
|
+
: T extends UnknownArray ? PickDeepArray<T, Path>
|
|
1389
|
+
: T extends object ? Simplify<PickDeepObject<T, Path>>
|
|
1390
|
+
: never;
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
Pick an object from the given object by one path.
|
|
1394
|
+
*/
|
|
1395
|
+
type PickDeepObject<RecordType extends object, P extends string | number> =
|
|
1396
|
+
P extends `${infer RecordKeyInPath}.${infer SubPath}`
|
|
1397
|
+
? ObjectValue<RecordType, RecordKeyInPath> extends infer ObjectV
|
|
1398
|
+
? IsNever<ObjectV> extends false
|
|
1399
|
+
? BuildObject<RecordKeyInPath, InternalPickDeep<NonNullable<ObjectV>, SubPath>, RecordType>
|
|
1400
|
+
: never
|
|
1401
|
+
: never
|
|
1402
|
+
: ObjectValue<RecordType, P> extends infer ObjectV
|
|
1403
|
+
? IsNever<ObjectV> extends false
|
|
1404
|
+
? BuildObject<P, ObjectV, RecordType>
|
|
1405
|
+
: never
|
|
1406
|
+
: never;
|
|
1407
|
+
|
|
1408
|
+
/**
|
|
1409
|
+
Pick an array from the given array by one path.
|
|
1410
|
+
*/
|
|
1411
|
+
type PickDeepArray<ArrayType extends UnknownArray, P extends string | number> =
|
|
1412
|
+
// Handle paths that are `${number}.${string}`
|
|
1413
|
+
P extends `${infer ArrayIndex extends number}.${infer SubPath}`
|
|
1414
|
+
// When `ArrayIndex` is equal to `number`
|
|
1415
|
+
? number extends ArrayIndex
|
|
1416
|
+
? ArrayType extends unknown[]
|
|
1417
|
+
? Array<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
|
|
1418
|
+
: ArrayType extends readonly unknown[]
|
|
1419
|
+
? ReadonlyArray<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
|
|
1420
|
+
: never
|
|
1421
|
+
// When `ArrayIndex` is a number literal
|
|
1422
|
+
: ArrayType extends unknown[]
|
|
1423
|
+
? [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
|
|
1424
|
+
: ArrayType extends readonly unknown[]
|
|
1425
|
+
? readonly [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
|
|
1426
|
+
: never
|
|
1427
|
+
// When the path is equal to `number`
|
|
1428
|
+
: P extends `${infer ArrayIndex extends number}`
|
|
1429
|
+
// When `ArrayIndex` is `number`
|
|
1430
|
+
? number extends ArrayIndex
|
|
1431
|
+
? ArrayType
|
|
1432
|
+
// When `ArrayIndex` is a number literal
|
|
1433
|
+
: ArrayType extends unknown[]
|
|
1434
|
+
? [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
|
|
1435
|
+
: ArrayType extends readonly unknown[]
|
|
1436
|
+
? readonly [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
|
|
1437
|
+
: never
|
|
1438
|
+
: never;
|
|
1439
|
+
|
|
1440
|
+
/**
|
|
1441
|
+
The implementation of `SplitArrayByIndex` for fixed length arrays.
|
|
1442
|
+
*/
|
|
1443
|
+
type SplitFixedArrayByIndex<T extends UnknownArray, SplitIndex extends number> =
|
|
1444
|
+
SplitIndex extends 0
|
|
1445
|
+
? [[], T]
|
|
1446
|
+
: T extends readonly [...BuildTuple<SplitIndex>, ...infer V]
|
|
1447
|
+
? T extends readonly [...infer U, ...V]
|
|
1448
|
+
? [U, V]
|
|
1449
|
+
: [never, never]
|
|
1450
|
+
: [never, never];
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
The implementation of `SplitArrayByIndex` for variable length arrays.
|
|
1454
|
+
*/
|
|
1455
|
+
type SplitVariableArrayByIndex<T extends UnknownArray,
|
|
1456
|
+
SplitIndex extends number,
|
|
1457
|
+
T1 = Subtract<SplitIndex, StaticPartOfArray<T>['length']>,
|
|
1458
|
+
T2 = T1 extends number ? BuildTuple<T1, VariablePartOfArray<T>[number]> : [],
|
|
1459
|
+
> =
|
|
1460
|
+
SplitIndex extends 0
|
|
1461
|
+
? [[], T]
|
|
1462
|
+
: GreaterThanOrEqual<StaticPartOfArray<T>['length'], SplitIndex> extends true
|
|
1463
|
+
? [
|
|
1464
|
+
SplitFixedArrayByIndex<StaticPartOfArray<T>, SplitIndex>[0],
|
|
1465
|
+
[
|
|
1466
|
+
...SplitFixedArrayByIndex<StaticPartOfArray<T>, SplitIndex>[1],
|
|
1467
|
+
...VariablePartOfArray<T>,
|
|
1468
|
+
],
|
|
1469
|
+
]
|
|
1470
|
+
: [
|
|
1471
|
+
[
|
|
1472
|
+
...StaticPartOfArray<T>,
|
|
1473
|
+
...(T2 extends UnknownArray ? T2 : []),
|
|
1474
|
+
],
|
|
1475
|
+
VariablePartOfArray<T>,
|
|
1476
|
+
];
|
|
1477
|
+
|
|
1478
|
+
/**
|
|
1479
|
+
Split the given array `T` by the given `SplitIndex`.
|
|
1480
|
+
|
|
1481
|
+
@example
|
|
1482
|
+
```
|
|
1483
|
+
type A = SplitArrayByIndex<[1, 2, 3, 4], 2>;
|
|
1484
|
+
// type A = [[1, 2], [3, 4]];
|
|
1485
|
+
|
|
1486
|
+
type B = SplitArrayByIndex<[1, 2, 3, 4], 0>;
|
|
1487
|
+
// type B = [[], [1, 2, 3, 4]];
|
|
1488
|
+
```
|
|
1489
|
+
*/
|
|
1490
|
+
type SplitArrayByIndex<T extends UnknownArray, SplitIndex extends number> =
|
|
1491
|
+
SplitIndex extends 0
|
|
1492
|
+
? [[], T]
|
|
1493
|
+
: number extends T['length']
|
|
1494
|
+
? SplitVariableArrayByIndex<T, SplitIndex>
|
|
1495
|
+
: SplitFixedArrayByIndex<T, SplitIndex>;
|
|
1496
|
+
|
|
1497
|
+
/**
|
|
1498
|
+
Creates a new array type by adding or removing elements at a specified index range in the original array.
|
|
1499
|
+
|
|
1500
|
+
Use-case: Replace or insert items in an array type.
|
|
1501
|
+
|
|
1502
|
+
Like [`Array#splice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) but for types.
|
|
1503
|
+
|
|
1504
|
+
@example
|
|
1505
|
+
```
|
|
1506
|
+
type SomeMonths0 = ['January', 'April', 'June'];
|
|
1507
|
+
type Mouths0 = ArraySplice<SomeMonths0, 1, 0, ['Feb', 'March']>;
|
|
1508
|
+
//=> type Mouths0 = ['January', 'Feb', 'March', 'April', 'June'];
|
|
1509
|
+
|
|
1510
|
+
type SomeMonths1 = ['January', 'April', 'June'];
|
|
1511
|
+
type Mouths1 = ArraySplice<SomeMonths1, 1, 1>;
|
|
1512
|
+
//=> type Mouths1 = ['January', 'June'];
|
|
1513
|
+
|
|
1514
|
+
type SomeMonths2 = ['January', 'Foo', 'April'];
|
|
1515
|
+
type Mouths2 = ArraySplice<SomeMonths2, 1, 1, ['Feb', 'March']>;
|
|
1516
|
+
//=> type Mouths2 = ['January', 'Feb', 'March', 'April'];
|
|
1517
|
+
```
|
|
1518
|
+
|
|
1519
|
+
@category Array
|
|
1520
|
+
*/
|
|
1521
|
+
type ArraySplice<
|
|
1522
|
+
T extends UnknownArray,
|
|
1523
|
+
Start extends number,
|
|
1524
|
+
DeleteCount extends number,
|
|
1525
|
+
Items extends UnknownArray = [],
|
|
1526
|
+
> =
|
|
1527
|
+
SplitArrayByIndex<T, Start> extends [infer U extends UnknownArray, infer V extends UnknownArray]
|
|
1528
|
+
? SplitArrayByIndex<V, DeleteCount> extends [infer _Deleted extends UnknownArray, infer X extends UnknownArray]
|
|
1529
|
+
? [...U, ...Items, ...X]
|
|
1530
|
+
: never // Should never happen
|
|
1531
|
+
: never; // Should never happen
|
|
1532
|
+
|
|
1533
|
+
/**
|
|
1534
|
+
Allows creating a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union.
|
|
1535
|
+
|
|
1536
|
+
Currently, when a union type of a primitive type is combined with literal types, TypeScript loses all information about the combined literals. Thus, when such type is used in an IDE with autocompletion, no suggestions are made for the declared literals.
|
|
1537
|
+
|
|
1538
|
+
This type is a workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). It will be removed as soon as it's not needed anymore.
|
|
1539
|
+
|
|
1540
|
+
@example
|
|
1541
|
+
```
|
|
1542
|
+
import type {LiteralUnion} from 'type-fest';
|
|
1543
|
+
|
|
1544
|
+
// Before
|
|
1545
|
+
|
|
1546
|
+
type Pet = 'dog' | 'cat' | string;
|
|
1547
|
+
|
|
1548
|
+
const pet: Pet = '';
|
|
1549
|
+
// Start typing in your TypeScript-enabled IDE.
|
|
1550
|
+
// You **will not** get auto-completion for `dog` and `cat` literals.
|
|
1551
|
+
|
|
1552
|
+
// After
|
|
1553
|
+
|
|
1554
|
+
type Pet2 = LiteralUnion<'dog' | 'cat', string>;
|
|
1555
|
+
|
|
1556
|
+
const pet: Pet2 = '';
|
|
1557
|
+
// You **will** get auto-completion for `dog` and `cat` literals.
|
|
1558
|
+
```
|
|
1559
|
+
|
|
1560
|
+
@category Type
|
|
1561
|
+
*/
|
|
1562
|
+
type LiteralUnion<
|
|
1563
|
+
LiteralType,
|
|
1564
|
+
BaseType extends Primitive,
|
|
1565
|
+
> = LiteralType | (BaseType & Record<never, never>);
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
SharedUnionFieldsDeep options.
|
|
1569
|
+
|
|
1570
|
+
@see {@link SharedUnionFieldsDeep}
|
|
1571
|
+
*/
|
|
1572
|
+
type SharedUnionFieldsDeepOptions = {
|
|
1573
|
+
/**
|
|
1574
|
+
When set to true, this option impacts each element within arrays or tuples. If all union values are arrays or tuples, it constructs an array of the shortest possible length, ensuring every element exists in the union array.
|
|
1575
|
+
|
|
1576
|
+
@default false
|
|
1577
|
+
*/
|
|
1578
|
+
recurseIntoArrays?: boolean;
|
|
1579
|
+
};
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
Create a type with shared fields from a union of object types, deeply traversing nested structures.
|
|
1583
|
+
|
|
1584
|
+
Use the {@link SharedUnionFieldsDeepOptions `Options`} to specify the behavior for arrays.
|
|
1585
|
+
|
|
1586
|
+
Use-cases:
|
|
1587
|
+
- You want a safe object type where each key exists in the union object.
|
|
1588
|
+
- You want to focus on the common fields of the union type and don't want to have to care about the other fields.
|
|
1589
|
+
|
|
1590
|
+
@example
|
|
1591
|
+
```
|
|
1592
|
+
import type {SharedUnionFieldsDeep} from 'type-fest';
|
|
1593
|
+
|
|
1594
|
+
type Cat = {
|
|
1595
|
+
info: {
|
|
1596
|
+
name: string;
|
|
1597
|
+
type: 'cat';
|
|
1598
|
+
catType: string;
|
|
1599
|
+
};
|
|
1600
|
+
};
|
|
1601
|
+
|
|
1602
|
+
type Dog = {
|
|
1603
|
+
info: {
|
|
1604
|
+
name: string;
|
|
1605
|
+
type: 'dog';
|
|
1606
|
+
dogType: string;
|
|
1607
|
+
};
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
function displayPetInfo(petInfo: (Cat | Dog)['info']) {
|
|
1611
|
+
// typeof petInfo =>
|
|
1612
|
+
// {
|
|
1613
|
+
// name: string;
|
|
1614
|
+
// type: 'cat';
|
|
1615
|
+
// catType: string; // Needn't care about this field, because it's not a common pet info field.
|
|
1616
|
+
// } | {
|
|
1617
|
+
// name: string;
|
|
1618
|
+
// type: 'dog';
|
|
1619
|
+
// dogType: string; // Needn't care about this field, because it's not a common pet info field.
|
|
1620
|
+
// }
|
|
1621
|
+
|
|
1622
|
+
// petInfo type is complex and have some needless fields
|
|
1623
|
+
|
|
1624
|
+
console.log('name: ', petInfo.name);
|
|
1625
|
+
console.log('type: ', petInfo.type);
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
function displayPetInfo(petInfo: SharedUnionFieldsDeep<Cat | Dog>['info']) {
|
|
1629
|
+
// typeof petInfo =>
|
|
1630
|
+
// {
|
|
1631
|
+
// name: string;
|
|
1632
|
+
// type: 'cat' | 'dog';
|
|
1633
|
+
// }
|
|
1634
|
+
|
|
1635
|
+
// petInfo type is simple and clear
|
|
1636
|
+
|
|
1637
|
+
console.log('name: ', petInfo.name);
|
|
1638
|
+
console.log('type: ', petInfo.type);
|
|
1639
|
+
}
|
|
1640
|
+
```
|
|
1641
|
+
|
|
1642
|
+
@category Object
|
|
1643
|
+
@category Union
|
|
1644
|
+
*/
|
|
1645
|
+
type SharedUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions = {recurseIntoArrays: false}> =
|
|
1646
|
+
// If `Union` is not a union type, return `Union` directly.
|
|
1647
|
+
IsUnion<Union> extends false
|
|
1648
|
+
? Union
|
|
1649
|
+
// `Union extends` will convert `Union`
|
|
1650
|
+
// to a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
|
|
1651
|
+
// But this is not what we want, so we need to wrap `Union` with `[]` to prevent it.
|
|
1652
|
+
: [Union] extends [NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>]
|
|
1653
|
+
? Union
|
|
1654
|
+
: [Union] extends [UnknownArray]
|
|
1655
|
+
? Options['recurseIntoArrays'] extends true
|
|
1656
|
+
? SetArrayAccess<SharedArrayUnionFieldsDeep<Union, Options>, IsArrayReadonly<Union>>
|
|
1657
|
+
: Union
|
|
1658
|
+
: [Union] extends [object]
|
|
1659
|
+
? SharedObjectUnionFieldsDeep<Union, Options>
|
|
1660
|
+
: Union;
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
Same as `SharedUnionFieldsDeep`, but accepts only `object`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
|
|
1664
|
+
*/
|
|
1665
|
+
type SharedObjectUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions> =
|
|
1666
|
+
keyof Union extends infer Keys
|
|
1667
|
+
? IsNever<Keys> extends false
|
|
1668
|
+
? {
|
|
1669
|
+
[Key in keyof Union]:
|
|
1670
|
+
Union[Key] extends NonRecursiveType
|
|
1671
|
+
? Union[Key]
|
|
1672
|
+
: SharedUnionFieldsDeep<Union[Key], Options>
|
|
1673
|
+
}
|
|
1674
|
+
: {}
|
|
1675
|
+
: Union;
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
Same as `SharedUnionFieldsDeep`, but accepts only `UnknownArray`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
|
|
1679
|
+
*/
|
|
1680
|
+
type SharedArrayUnionFieldsDeep<Union extends UnknownArray, Options extends SharedUnionFieldsDeepOptions> =
|
|
1681
|
+
// Restore the readonly modifier of the array.
|
|
1682
|
+
SetArrayAccess<
|
|
1683
|
+
InternalSharedArrayUnionFieldsDeep<Union, Options>,
|
|
1684
|
+
IsArrayReadonly<Union>
|
|
1685
|
+
>;
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
Internal helper for `SharedArrayUnionFieldsDeep`. Needn't care the `readonly` modifier of arrays.
|
|
1689
|
+
*/
|
|
1690
|
+
type InternalSharedArrayUnionFieldsDeep<
|
|
1691
|
+
Union extends UnknownArray,
|
|
1692
|
+
Options extends SharedUnionFieldsDeepOptions,
|
|
1693
|
+
ResultTuple extends UnknownArray = [],
|
|
1694
|
+
> =
|
|
1695
|
+
// We should build a minimum possible length tuple where each element in the tuple exists in the union tuple.
|
|
1696
|
+
IsNever<TupleLength<Union>> extends true
|
|
1697
|
+
// Rule 1: If all the arrays in the union have non-fixed lengths,
|
|
1698
|
+
// like `Array<string> | [number, ...string[]]`
|
|
1699
|
+
// we should build a tuple that is [the_fixed_parts_of_union, ...the_rest_of_union[]].
|
|
1700
|
+
// For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, ...string[]]>`
|
|
1701
|
+
// => `[string | number, ...string[]]`.
|
|
1702
|
+
? ResultTuple['length'] extends UnionMax<StaticPartOfArray<Union>['length']>
|
|
1703
|
+
? [
|
|
1704
|
+
// The fixed-length part of the tuple.
|
|
1705
|
+
...ResultTuple,
|
|
1706
|
+
// The rest of the union.
|
|
1707
|
+
// Due to `ResultTuple` is the maximum possible fixed-length part of the tuple,
|
|
1708
|
+
// so we can use `StaticPartOfArray` to get the rest of the union.
|
|
1709
|
+
...Array<
|
|
1710
|
+
SharedUnionFieldsDeep<VariablePartOfArray<Union>[number], Options>
|
|
1711
|
+
>,
|
|
1712
|
+
]
|
|
1713
|
+
// Build the fixed-length tuple recursively.
|
|
1714
|
+
: InternalSharedArrayUnionFieldsDeep<
|
|
1715
|
+
Union, Options,
|
|
1716
|
+
[...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
|
|
1717
|
+
>
|
|
1718
|
+
// Rule 2: If at least one of the arrays in the union have fixed lengths,
|
|
1719
|
+
// like `Array<string> | [number, string]`,
|
|
1720
|
+
// we should build a tuple of the smallest possible length to ensure any
|
|
1721
|
+
// item in the result tuple exists in the union tuple.
|
|
1722
|
+
// For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, string]>`
|
|
1723
|
+
// => `[string | number, string]`.
|
|
1724
|
+
: ResultTuple['length'] extends UnionMin<TupleLength<Union>>
|
|
1725
|
+
? ResultTuple
|
|
1726
|
+
// As above, build tuple recursively.
|
|
1727
|
+
: InternalSharedArrayUnionFieldsDeep<
|
|
1728
|
+
Union, Options,
|
|
1729
|
+
[...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
|
|
1730
|
+
>;
|
|
1731
|
+
|
|
1732
|
+
/**
|
|
1733
|
+
Omit properties from a deeply-nested object.
|
|
1734
|
+
|
|
1735
|
+
It supports recursing into arrays.
|
|
1736
|
+
|
|
1737
|
+
It supports removing specific items from an array, replacing each removed item with unknown at the specified index.
|
|
1738
|
+
|
|
1739
|
+
Use-case: Remove unneeded parts of complex objects.
|
|
1740
|
+
|
|
1741
|
+
Use [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys) if you only need one level deep.
|
|
1742
|
+
|
|
1743
|
+
@example
|
|
1744
|
+
```
|
|
1745
|
+
import type {OmitDeep} from 'type-fest';
|
|
1746
|
+
|
|
1747
|
+
type Info = {
|
|
1748
|
+
userInfo: {
|
|
1749
|
+
name: string;
|
|
1750
|
+
uselessInfo: {
|
|
1751
|
+
foo: string;
|
|
1752
|
+
};
|
|
1753
|
+
};
|
|
1754
|
+
};
|
|
1755
|
+
|
|
1756
|
+
type UsefulInfo = OmitDeep<Info, 'userInfo.uselessInfo'>;
|
|
1757
|
+
// type UsefulInfo = {
|
|
1758
|
+
// userInfo: {
|
|
1759
|
+
// name: string;
|
|
1760
|
+
// };
|
|
1761
|
+
|
|
1762
|
+
// Supports array
|
|
1763
|
+
type A = OmitDeep<[1, 'foo', 2], 1>;
|
|
1764
|
+
// type A = [1, unknown, 2];
|
|
1765
|
+
|
|
1766
|
+
// Supports recursing into array
|
|
1767
|
+
|
|
1768
|
+
type Info1 = {
|
|
1769
|
+
address: [
|
|
1770
|
+
{
|
|
1771
|
+
street: string
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
street2: string,
|
|
1775
|
+
foo: string
|
|
1776
|
+
};
|
|
1777
|
+
];
|
|
1778
|
+
}
|
|
1779
|
+
type AddressInfo = OmitDeep<Info1, 'address.1.foo'>;
|
|
1780
|
+
// type AddressInfo = {
|
|
1781
|
+
// address: [
|
|
1782
|
+
// {
|
|
1783
|
+
// street: string;
|
|
1784
|
+
// },
|
|
1785
|
+
// {
|
|
1786
|
+
// street2: string;
|
|
1787
|
+
// };
|
|
1788
|
+
// ];
|
|
1789
|
+
// };
|
|
1790
|
+
```
|
|
1791
|
+
|
|
1792
|
+
@category Object
|
|
1793
|
+
@category Array
|
|
1794
|
+
*/
|
|
1795
|
+
type OmitDeep<T, PathUnion extends LiteralUnion<Paths<T>, string>> =
|
|
1796
|
+
SimplifyDeep<
|
|
1797
|
+
SharedUnionFieldsDeep<
|
|
1798
|
+
{[P in PathUnion]: OmitDeepWithOnePath<T, P>}[PathUnion]
|
|
1799
|
+
>,
|
|
1800
|
+
UnknownArray>;
|
|
1801
|
+
|
|
1802
|
+
/**
|
|
1803
|
+
Omit one path from the given object/array.
|
|
1804
|
+
*/
|
|
1805
|
+
type OmitDeepWithOnePath<T, Path extends string | number> =
|
|
1806
|
+
T extends NonRecursiveType
|
|
1807
|
+
? T
|
|
1808
|
+
: T extends UnknownArray ? SetArrayAccess<OmitDeepArrayWithOnePath<T, Path>, IsArrayReadonly<T>>
|
|
1809
|
+
: T extends object ? OmitDeepObjectWithOnePath<T, Path>
|
|
1810
|
+
: T;
|
|
1811
|
+
|
|
1812
|
+
/**
|
|
1813
|
+
Omit one path from the given object.
|
|
1814
|
+
*/
|
|
1815
|
+
type OmitDeepObjectWithOnePath<ObjectT extends object, P extends string | number> =
|
|
1816
|
+
P extends `${infer RecordKeyInPath}.${infer SubPath}`
|
|
1817
|
+
? {
|
|
1818
|
+
[Key in keyof ObjectT]:
|
|
1819
|
+
IsEqual<RecordKeyInPath, ToString<Key>> extends true
|
|
1820
|
+
? ExactKey<ObjectT, Key> extends infer RealKey
|
|
1821
|
+
? RealKey extends keyof ObjectT
|
|
1822
|
+
? OmitDeepWithOnePath<ObjectT[RealKey], SubPath>
|
|
1823
|
+
: ObjectT[Key]
|
|
1824
|
+
: ObjectT[Key]
|
|
1825
|
+
: ObjectT[Key]
|
|
1826
|
+
}
|
|
1827
|
+
: ExactKey<ObjectT, P> extends infer Key
|
|
1828
|
+
? IsNever<Key> extends true
|
|
1829
|
+
? ObjectT
|
|
1830
|
+
: Key extends PropertyKey
|
|
1831
|
+
? Omit<ObjectT, Key>
|
|
1832
|
+
: ObjectT
|
|
1833
|
+
: ObjectT;
|
|
1834
|
+
|
|
1835
|
+
/**
|
|
1836
|
+
Omit one path from from the given array.
|
|
1837
|
+
|
|
1838
|
+
It replaces the item to `unknown` at the given index.
|
|
1839
|
+
|
|
1840
|
+
@example
|
|
1841
|
+
```
|
|
1842
|
+
type A = OmitDeepArrayWithOnePath<[10, 20, 30, 40], 2>;
|
|
1843
|
+
//=> type A = [10, 20, unknown, 40];
|
|
1844
|
+
```
|
|
1845
|
+
*/
|
|
1846
|
+
type OmitDeepArrayWithOnePath<ArrayType extends UnknownArray, P extends string | number> =
|
|
1847
|
+
// Handle paths that are `${number}.${string}`
|
|
1848
|
+
P extends `${infer ArrayIndex extends number}.${infer SubPath}`
|
|
1849
|
+
// If `ArrayIndex` is equal to `number`
|
|
1850
|
+
? number extends ArrayIndex
|
|
1851
|
+
? Array<OmitDeepWithOnePath<NonNullable<ArrayType[number]>, SubPath>>
|
|
1852
|
+
// If `ArrayIndex` is a number literal
|
|
1853
|
+
: ArraySplice<ArrayType, ArrayIndex, 1, [OmitDeepWithOnePath<NonNullable<ArrayType[ArrayIndex]>, SubPath>]>
|
|
1854
|
+
// If the path is equal to `number`
|
|
1855
|
+
: P extends `${infer ArrayIndex extends number}`
|
|
1856
|
+
// If `ArrayIndex` is `number`
|
|
1857
|
+
? number extends ArrayIndex
|
|
1858
|
+
? []
|
|
1859
|
+
// If `ArrayIndex` is a number literal
|
|
1860
|
+
: ArraySplice<ArrayType, ArrayIndex, 1, [unknown]>
|
|
1861
|
+
: ArrayType;
|
|
1862
|
+
|
|
1863
|
+
/**
|
|
1864
|
+
Get keys of the given type as strings.
|
|
1865
|
+
|
|
1866
|
+
Number keys are converted to strings.
|
|
1867
|
+
|
|
1868
|
+
Use-cases:
|
|
1869
|
+
- Get string keys from a type which may have number keys.
|
|
1870
|
+
- Makes it possible to index using strings retrieved from template types.
|
|
1871
|
+
|
|
1872
|
+
@example
|
|
1873
|
+
```
|
|
1874
|
+
import type {StringKeyOf} from 'type-fest';
|
|
1875
|
+
|
|
1876
|
+
type Foo = {
|
|
1877
|
+
1: number,
|
|
1878
|
+
stringKey: string,
|
|
1879
|
+
};
|
|
1880
|
+
|
|
1881
|
+
type StringKeysOfFoo = StringKeyOf<Foo>;
|
|
1882
|
+
//=> '1' | 'stringKey'
|
|
1883
|
+
```
|
|
1884
|
+
|
|
1885
|
+
@category Object
|
|
1886
|
+
*/
|
|
1887
|
+
type StringKeyOf<BaseType> = `${Extract<keyof BaseType, string | number>}`;
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
Represents an array of strings split using a given character or character set.
|
|
1891
|
+
|
|
1892
|
+
Use-case: Defining the return type of a method like `String.prototype.split`.
|
|
1893
|
+
|
|
1894
|
+
@example
|
|
1895
|
+
```
|
|
1896
|
+
import type {Split} from 'type-fest';
|
|
1897
|
+
|
|
1898
|
+
declare function split<S extends string, D extends string>(string: S, separator: D): Split<S, D>;
|
|
1899
|
+
|
|
1900
|
+
type Item = 'foo' | 'bar' | 'baz' | 'waldo';
|
|
1901
|
+
const items = 'foo,bar,baz,waldo';
|
|
1902
|
+
let array: Item[];
|
|
1903
|
+
|
|
1904
|
+
array = split(items, ',');
|
|
1905
|
+
```
|
|
1906
|
+
|
|
1907
|
+
@category String
|
|
1908
|
+
@category Template literal
|
|
1909
|
+
*/
|
|
1910
|
+
type Split<
|
|
1911
|
+
S extends string,
|
|
1912
|
+
Delimiter extends string,
|
|
1913
|
+
> = S extends `${infer Head}${Delimiter}${infer Tail}`
|
|
1914
|
+
? [Head, ...Split<Tail, Delimiter>]
|
|
1915
|
+
: S extends Delimiter
|
|
1916
|
+
? []
|
|
1917
|
+
: [S];
|
|
1918
|
+
|
|
1919
|
+
type GetOptions = {
|
|
1920
|
+
/**
|
|
1921
|
+
Include `undefined` in the return type when accessing properties.
|
|
1922
|
+
|
|
1923
|
+
Setting this to `false` is not recommended.
|
|
1924
|
+
|
|
1925
|
+
@default true
|
|
1926
|
+
*/
|
|
1927
|
+
strict?: boolean;
|
|
1928
|
+
};
|
|
1929
|
+
|
|
1930
|
+
/**
|
|
1931
|
+
Like the `Get` type but receives an array of strings as a path parameter.
|
|
1932
|
+
*/
|
|
1933
|
+
type GetWithPath<BaseType, Keys extends readonly string[], Options extends GetOptions = {}> =
|
|
1934
|
+
Keys extends readonly []
|
|
1935
|
+
? BaseType
|
|
1936
|
+
: Keys extends readonly [infer Head, ...infer Tail]
|
|
1937
|
+
? GetWithPath<
|
|
1938
|
+
PropertyOf<BaseType, Extract<Head, string>, Options>,
|
|
1939
|
+
Extract<Tail, string[]>,
|
|
1940
|
+
Options
|
|
1941
|
+
>
|
|
1942
|
+
: never;
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
Adds `undefined` to `Type` if `strict` is enabled.
|
|
1946
|
+
*/
|
|
1947
|
+
type Strictify<Type, Options extends GetOptions> =
|
|
1948
|
+
Options['strict'] extends false ? Type : (Type | undefined);
|
|
1949
|
+
|
|
1950
|
+
/**
|
|
1951
|
+
If `Options['strict']` is `true`, includes `undefined` in the returned type when accessing properties on `Record<string, any>`.
|
|
1952
|
+
|
|
1953
|
+
Known limitations:
|
|
1954
|
+
- Does not include `undefined` in the type on object types with an index signature (for example, `{a: string; [key: string]: string}`).
|
|
1955
|
+
*/
|
|
1956
|
+
type StrictPropertyOf<BaseType, Key extends keyof BaseType, Options extends GetOptions> =
|
|
1957
|
+
Record<string, any> extends BaseType
|
|
1958
|
+
? string extends keyof BaseType
|
|
1959
|
+
? Strictify<BaseType[Key], Options> // Record<string, any>
|
|
1960
|
+
: BaseType[Key] // Record<'a' | 'b', any> (Records with a string union as keys have required properties)
|
|
1961
|
+
: BaseType[Key];
|
|
1962
|
+
|
|
1963
|
+
/**
|
|
1964
|
+
Splits a dot-prop style path into a tuple comprised of the properties in the path. Handles square-bracket notation.
|
|
1965
|
+
|
|
1966
|
+
@example
|
|
1967
|
+
```
|
|
1968
|
+
ToPath<'foo.bar.baz'>
|
|
1969
|
+
//=> ['foo', 'bar', 'baz']
|
|
1970
|
+
|
|
1971
|
+
ToPath<'foo[0].bar.baz'>
|
|
1972
|
+
//=> ['foo', '0', 'bar', 'baz']
|
|
1973
|
+
```
|
|
1974
|
+
*/
|
|
1975
|
+
type ToPath<S extends string> = Split<FixPathSquareBrackets<S>, '.'>;
|
|
1976
|
+
|
|
1977
|
+
/**
|
|
1978
|
+
Replaces square-bracketed dot notation with dots, for example, `foo[0].bar` -> `foo.0.bar`.
|
|
1979
|
+
*/
|
|
1980
|
+
type FixPathSquareBrackets<Path extends string> =
|
|
1981
|
+
Path extends `[${infer Head}]${infer Tail}`
|
|
1982
|
+
? Tail extends `[${string}`
|
|
1983
|
+
? `${Head}.${FixPathSquareBrackets<Tail>}`
|
|
1984
|
+
: `${Head}${FixPathSquareBrackets<Tail>}`
|
|
1985
|
+
: Path extends `${infer Head}[${infer Middle}]${infer Tail}`
|
|
1986
|
+
? `${Head}.${FixPathSquareBrackets<`[${Middle}]${Tail}`>}`
|
|
1987
|
+
: Path;
|
|
1988
|
+
|
|
1989
|
+
/**
|
|
1990
|
+
Returns true if `LongString` is made up out of `Substring` repeated 0 or more times.
|
|
1991
|
+
|
|
1992
|
+
@example
|
|
1993
|
+
```
|
|
1994
|
+
ConsistsOnlyOf<'aaa', 'a'> //=> true
|
|
1995
|
+
ConsistsOnlyOf<'ababab', 'ab'> //=> true
|
|
1996
|
+
ConsistsOnlyOf<'aBa', 'a'> //=> false
|
|
1997
|
+
ConsistsOnlyOf<'', 'a'> //=> true
|
|
1998
|
+
```
|
|
1999
|
+
*/
|
|
2000
|
+
type ConsistsOnlyOf<LongString extends string, Substring extends string> =
|
|
2001
|
+
LongString extends ''
|
|
2002
|
+
? true
|
|
2003
|
+
: LongString extends `${Substring}${infer Tail}`
|
|
2004
|
+
? ConsistsOnlyOf<Tail, Substring>
|
|
2005
|
+
: false;
|
|
2006
|
+
|
|
2007
|
+
/**
|
|
2008
|
+
Convert a type which may have number keys to one with string keys, making it possible to index using strings retrieved from template types.
|
|
2009
|
+
|
|
2010
|
+
@example
|
|
2011
|
+
```
|
|
2012
|
+
type WithNumbers = {foo: string; 0: boolean};
|
|
2013
|
+
type WithStrings = WithStringKeys<WithNumbers>;
|
|
2014
|
+
|
|
2015
|
+
type WithNumbersKeys = keyof WithNumbers;
|
|
2016
|
+
//=> 'foo' | 0
|
|
2017
|
+
type WithStringsKeys = keyof WithStrings;
|
|
2018
|
+
//=> 'foo' | '0'
|
|
2019
|
+
```
|
|
2020
|
+
*/
|
|
2021
|
+
type WithStringKeys<BaseType> = {
|
|
2022
|
+
[Key in StringKeyOf<BaseType>]: UncheckedIndex<BaseType, Key>
|
|
2023
|
+
};
|
|
2024
|
+
|
|
2025
|
+
/**
|
|
2026
|
+
Perform a `T[U]` operation if `T` supports indexing.
|
|
2027
|
+
*/
|
|
2028
|
+
type UncheckedIndex<T, U extends string | number> = [T] extends [Record<string | number, any>] ? T[U] : never;
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
Get a property of an object or array. Works when indexing arrays using number-literal-strings, for example, `PropertyOf<number[], '0'> = number`, and when indexing objects with number keys.
|
|
2032
|
+
|
|
2033
|
+
Note:
|
|
2034
|
+
- Returns `unknown` if `Key` is not a property of `BaseType`, since TypeScript uses structural typing, and it cannot be guaranteed that extra properties unknown to the type system will exist at runtime.
|
|
2035
|
+
- Returns `undefined` from nullish values, to match the behaviour of most deep-key libraries like `lodash`, `dot-prop`, etc.
|
|
2036
|
+
*/
|
|
2037
|
+
type PropertyOf<BaseType, Key extends string, Options extends GetOptions = {}> =
|
|
2038
|
+
BaseType extends null | undefined
|
|
2039
|
+
? undefined
|
|
2040
|
+
: Key extends keyof BaseType
|
|
2041
|
+
? StrictPropertyOf<BaseType, Key, Options>
|
|
2042
|
+
: BaseType extends readonly [] | readonly [unknown, ...unknown[]]
|
|
2043
|
+
? unknown // It's a tuple, but `Key` did not extend `keyof BaseType`. So the index is out of bounds.
|
|
2044
|
+
: BaseType extends {
|
|
2045
|
+
[n: number]: infer Item;
|
|
2046
|
+
length: number; // Note: This is needed to avoid being too lax with records types using number keys like `{0: string; 1: boolean}`.
|
|
2047
|
+
}
|
|
2048
|
+
? (
|
|
2049
|
+
ConsistsOnlyOf<Key, StringDigit> extends true
|
|
2050
|
+
? Strictify<Item, Options>
|
|
2051
|
+
: unknown
|
|
2052
|
+
)
|
|
2053
|
+
: Key extends keyof WithStringKeys<BaseType>
|
|
2054
|
+
? StrictPropertyOf<WithStringKeys<BaseType>, Key, Options>
|
|
2055
|
+
: unknown;
|
|
2056
|
+
|
|
2057
|
+
// This works by first splitting the path based on `.` and `[...]` characters into a tuple of string keys. Then it recursively uses the head key to get the next property of the current object, until there are no keys left. Number keys extract the item type from arrays, or are converted to strings to extract types from tuples and dictionaries with number keys.
|
|
2058
|
+
/**
|
|
2059
|
+
Get a deeply-nested property from an object using a key path, like Lodash's `.get()` function.
|
|
2060
|
+
|
|
2061
|
+
Use-case: Retrieve a property from deep inside an API response or some other complex object.
|
|
2062
|
+
|
|
2063
|
+
@example
|
|
2064
|
+
```
|
|
2065
|
+
import type {Get} from 'type-fest';
|
|
2066
|
+
import * as lodash from 'lodash';
|
|
2067
|
+
|
|
2068
|
+
const get = <BaseType, Path extends string | readonly string[]>(object: BaseType, path: Path): Get<BaseType, Path> =>
|
|
2069
|
+
lodash.get(object, path);
|
|
2070
|
+
|
|
2071
|
+
interface ApiResponse {
|
|
2072
|
+
hits: {
|
|
2073
|
+
hits: Array<{
|
|
2074
|
+
_id: string
|
|
2075
|
+
_source: {
|
|
2076
|
+
name: Array<{
|
|
2077
|
+
given: string[]
|
|
2078
|
+
family: string
|
|
2079
|
+
}>
|
|
2080
|
+
birthDate: string
|
|
2081
|
+
}
|
|
2082
|
+
}>
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
const getName = (apiResponse: ApiResponse) =>
|
|
2087
|
+
get(apiResponse, 'hits.hits[0]._source.name');
|
|
2088
|
+
//=> Array<{given: string[]; family: string}> | undefined
|
|
2089
|
+
|
|
2090
|
+
// Path also supports a readonly array of strings
|
|
2091
|
+
const getNameWithPathArray = (apiResponse: ApiResponse) =>
|
|
2092
|
+
get(apiResponse, ['hits','hits', '0', '_source', 'name'] as const);
|
|
2093
|
+
//=> Array<{given: string[]; family: string}> | undefined
|
|
2094
|
+
|
|
2095
|
+
// Non-strict mode:
|
|
2096
|
+
Get<string[], '3', {strict: false}> //=> string
|
|
2097
|
+
Get<Record<string, string>, 'foo', {strict: true}> // => string
|
|
2098
|
+
```
|
|
2099
|
+
|
|
2100
|
+
@category Object
|
|
2101
|
+
@category Array
|
|
2102
|
+
@category Template literal
|
|
2103
|
+
*/
|
|
2104
|
+
type Get<BaseType, Path extends string | readonly string[], Options extends GetOptions = {}> =
|
|
2105
|
+
GetWithPath<BaseType, Path extends string ? ToPath<Path> : Path, Options>;
|
|
2106
|
+
|
|
2107
|
+
declare const omit: <T extends { [key in string]: unknown; }, K extends string>(object: T, keys: Paths<T>[]) => OmitDeep<T, K>;
|
|
2108
|
+
|
|
2109
|
+
declare const pick: <T extends { [key in string]: unknown; }, K extends Paths<T>>(object: T, keys: Paths<T>[]) => PickDeep<T, K>;
|
|
2110
|
+
|
|
2111
|
+
interface DeeksOptions {
|
|
2112
|
+
/** @default false */
|
|
2113
|
+
arrayIndexesAsKeys?: boolean;
|
|
2114
|
+
/** @default true */
|
|
2115
|
+
expandNestedObjects?: boolean;
|
|
2116
|
+
/** @default false */
|
|
2117
|
+
expandArrayObjects?: boolean;
|
|
2118
|
+
/** @default false */
|
|
2119
|
+
ignoreEmptyArraysWhenExpanding?: boolean;
|
|
2120
|
+
/** @default false */
|
|
2121
|
+
escapeNestedDots?: boolean;
|
|
2122
|
+
/** @default false */
|
|
2123
|
+
ignoreEmptyArrays?: boolean;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* Return the deep keys list for a single document
|
|
2128
|
+
* @param object
|
|
2129
|
+
* @param options
|
|
2130
|
+
* @returns {Array}
|
|
2131
|
+
*/
|
|
2132
|
+
declare function deepKeys(object: object, options?: DeeksOptions): string[];
|
|
2133
|
+
/**
|
|
2134
|
+
* Return the deep keys list for all documents in the provided list
|
|
2135
|
+
* @param list
|
|
2136
|
+
* @param options
|
|
2137
|
+
* @returns Array[Array[String]]
|
|
2138
|
+
*/
|
|
2139
|
+
declare function deepKeysFromList(list: object[], options?: DeeksOptions): string[][];
|
|
2140
|
+
|
|
2141
|
+
/**
|
|
2142
|
+
Get the value of the property at the given path.
|
|
2143
|
+
|
|
2144
|
+
@param object - Object or array to get the `path` value.
|
|
2145
|
+
@param path - Path of the property in the object, using `.` to separate each nested key. Use `\\.` if you have a `.` in the key.
|
|
2146
|
+
@param defaultValue - Default value.
|
|
2147
|
+
|
|
2148
|
+
@example
|
|
2149
|
+
```
|
|
2150
|
+
import {getProperty} from 'dot-prop';
|
|
2151
|
+
|
|
2152
|
+
getProperty({foo: {bar: 'unicorn'}}, 'foo.bar');
|
|
2153
|
+
//=> 'unicorn'
|
|
2154
|
+
|
|
2155
|
+
getProperty({foo: {bar: 'a'}}, 'foo.notDefined.deep');
|
|
2156
|
+
//=> undefined
|
|
2157
|
+
|
|
2158
|
+
getProperty({foo: {bar: 'a'}}, 'foo.notDefined.deep', 'default value');
|
|
2159
|
+
//=> 'default value'
|
|
2160
|
+
|
|
2161
|
+
getProperty({foo: {'dot.dot': 'unicorn'}}, 'foo.dot\\.dot');
|
|
2162
|
+
//=> 'unicorn'
|
|
2163
|
+
|
|
2164
|
+
getProperty({foo: [{bar: 'unicorn'}]}, 'foo[0].bar');
|
|
2165
|
+
//=> 'unicorn'
|
|
2166
|
+
```
|
|
2167
|
+
*/
|
|
2168
|
+
declare function getProperty<ObjectType, PathType extends string, DefaultValue = undefined>(
|
|
2169
|
+
object: ObjectType,
|
|
2170
|
+
path: PathType,
|
|
2171
|
+
defaultValue?: DefaultValue
|
|
2172
|
+
): ObjectType extends Record<string, unknown> | unknown[] ? (unknown extends Get<ObjectType, PathType> ? DefaultValue : Get<ObjectType, PathType>) : undefined;
|
|
2173
|
+
|
|
2174
|
+
/**
|
|
2175
|
+
Set the property at the given path to the given value.
|
|
2176
|
+
|
|
2177
|
+
@param object - Object or array to set the `path` value.
|
|
2178
|
+
@param path - Path of the property in the object, using `.` to separate each nested key. Use `\\.` if you have a `.` in the key.
|
|
2179
|
+
@param value - Value to set at `path`.
|
|
2180
|
+
@returns The object.
|
|
2181
|
+
|
|
2182
|
+
@example
|
|
2183
|
+
```
|
|
2184
|
+
import {setProperty} from 'dot-prop';
|
|
2185
|
+
|
|
2186
|
+
const object = {foo: {bar: 'a'}};
|
|
2187
|
+
setProperty(object, 'foo.bar', 'b');
|
|
2188
|
+
console.log(object);
|
|
2189
|
+
//=> {foo: {bar: 'b'}}
|
|
2190
|
+
|
|
2191
|
+
const foo = setProperty({}, 'foo.bar', 'c');
|
|
2192
|
+
console.log(foo);
|
|
2193
|
+
//=> {foo: {bar: 'c'}}
|
|
2194
|
+
|
|
2195
|
+
setProperty(object, 'foo.baz', 'x');
|
|
2196
|
+
console.log(object);
|
|
2197
|
+
//=> {foo: {bar: 'b', baz: 'x'}}
|
|
2198
|
+
|
|
2199
|
+
setProperty(object, 'foo.biz[0]', 'a');
|
|
2200
|
+
console.log(object);
|
|
2201
|
+
//=> {foo: {bar: 'b', baz: 'x', biz: ['a']}}
|
|
2202
|
+
```
|
|
2203
|
+
*/
|
|
2204
|
+
declare function setProperty<ObjectType extends Record<string, any>>(
|
|
2205
|
+
object: ObjectType,
|
|
2206
|
+
path: string,
|
|
2207
|
+
value: unknown
|
|
2208
|
+
): ObjectType;
|
|
2209
|
+
|
|
2210
|
+
/**
|
|
2211
|
+
Check whether the property at the given path exists.
|
|
2212
|
+
|
|
2213
|
+
@param object - Object or array to test the `path` value.
|
|
2214
|
+
@param path - Path of the property in the object, using `.` to separate each nested key. Use `\\.` if you have a `.` in the key.
|
|
2215
|
+
|
|
2216
|
+
@example
|
|
2217
|
+
```
|
|
2218
|
+
import {hasProperty} from 'dot-prop';
|
|
2219
|
+
|
|
2220
|
+
hasProperty({foo: {bar: 'unicorn'}}, 'foo.bar');
|
|
2221
|
+
//=> true
|
|
2222
|
+
```
|
|
2223
|
+
*/
|
|
2224
|
+
declare function hasProperty(object: Record<string, any> | undefined, path: string): boolean;
|
|
2225
|
+
|
|
2226
|
+
/**
|
|
2227
|
+
Delete the property at the given path.
|
|
2228
|
+
|
|
2229
|
+
@param object - Object or array to delete the `path` value.
|
|
2230
|
+
@param path - Path of the property in the object, using `.` to separate each nested key. Use `\\.` if you have a `.` in the key.
|
|
2231
|
+
@returns A boolean of whether the property existed before being deleted.
|
|
2232
|
+
|
|
2233
|
+
@example
|
|
2234
|
+
```
|
|
2235
|
+
import {deleteProperty} from 'dot-prop';
|
|
2236
|
+
|
|
2237
|
+
const object = {foo: {bar: 'a'}};
|
|
2238
|
+
deleteProperty(object, 'foo.bar');
|
|
2239
|
+
console.log(object);
|
|
2240
|
+
//=> {foo: {}}
|
|
2241
|
+
|
|
2242
|
+
object.foo.bar = {x: 'y', y: 'x'};
|
|
2243
|
+
deleteProperty(object, 'foo.bar.x');
|
|
2244
|
+
console.log(object);
|
|
2245
|
+
//=> {foo: {bar: {y: 'x'}}}
|
|
2246
|
+
```
|
|
2247
|
+
*/
|
|
2248
|
+
declare function deleteProperty(object: Record<string, any>, path: string): boolean;
|
|
2249
|
+
|
|
2250
|
+
/**
|
|
2251
|
+
Escape special characters in a path. Useful for sanitizing user input.
|
|
2252
|
+
|
|
2253
|
+
@param path - The dot path to sanitize.
|
|
2254
|
+
|
|
2255
|
+
@example
|
|
2256
|
+
```
|
|
2257
|
+
import {getProperty, escapePath} from 'dot-prop';
|
|
2258
|
+
|
|
2259
|
+
const object = {
|
|
2260
|
+
foo: {
|
|
2261
|
+
bar: 'πΈπ» You found me Mario!',
|
|
2262
|
+
},
|
|
2263
|
+
'foo.bar' : 'π The princess is in another castle!',
|
|
2264
|
+
};
|
|
2265
|
+
const escapedPath = escapePath('foo.bar');
|
|
2266
|
+
|
|
2267
|
+
console.log(getProperty(object, escapedPath));
|
|
2268
|
+
//=> 'π The princess is in another castle!'
|
|
2269
|
+
```
|
|
2270
|
+
*/
|
|
2271
|
+
declare function escapePath(path: string): string;
|
|
2272
|
+
|
|
2273
|
+
/**
|
|
2274
|
+
Check if a value is a plain object.
|
|
2275
|
+
|
|
2276
|
+
An object is plain if it's created by either `{}`, `new Object()`, or `Object.create(null)`.
|
|
2277
|
+
|
|
2278
|
+
@example
|
|
2279
|
+
```
|
|
2280
|
+
import isPlainObject from 'is-plain-obj';
|
|
2281
|
+
import {runInNewContext} from 'node:vm';
|
|
2282
|
+
|
|
2283
|
+
isPlainObject({foo: 'bar'});
|
|
2284
|
+
//=> true
|
|
2285
|
+
|
|
2286
|
+
isPlainObject(new Object());
|
|
2287
|
+
//=> true
|
|
2288
|
+
|
|
2289
|
+
isPlainObject(Object.create(null));
|
|
2290
|
+
//=> true
|
|
2291
|
+
|
|
2292
|
+
// This works across realms
|
|
2293
|
+
isPlainObject(runInNewContext('({})'));
|
|
2294
|
+
//=> true
|
|
2295
|
+
|
|
2296
|
+
isPlainObject([1, 2, 3]);
|
|
2297
|
+
//=> false
|
|
2298
|
+
|
|
2299
|
+
class Unicorn {}
|
|
2300
|
+
isPlainObject(new Unicorn());
|
|
2301
|
+
//=> false
|
|
2302
|
+
|
|
2303
|
+
isPlainObject(Math);
|
|
2304
|
+
//=> false
|
|
2305
|
+
```
|
|
2306
|
+
*/
|
|
2307
|
+
declare function isPlainObject<Value>(value: unknown): value is Record<PropertyKey, Value>;
|
|
2308
|
+
|
|
2309
|
+
export { type DeeksOptions as DeepKeysOptions, type OmitDeep, type Paths, type PickDeep, type Split, deepKeys, deepKeysFromList, deleteProperty, escapePath, getProperty, hasProperty, isPlainObject, omit, pick, setProperty };
|