type-fest 4.8.3 → 4.10.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/index.d.ts CHANGED
@@ -105,6 +105,7 @@ export type {ArrayIndices} from './source/array-indices';
105
105
  export type {ArrayValues} from './source/array-values';
106
106
  export type {SetFieldType} from './source/set-field-type';
107
107
  export type {Paths} from './source/paths';
108
+ export type {SharedUnionFieldsDeep} from './source/shared-union-fields-deep';
108
109
 
109
110
  // Template literal types
110
111
  export type {CamelCase} from './source/camel-case';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "type-fest",
3
- "version": "4.8.3",
3
+ "version": "4.10.0",
4
4
  "description": "A collection of essential TypeScript types",
5
5
  "license": "(MIT OR CC0-1.0)",
6
6
  "repository": "sindresorhus/type-fest",
package/readme.md CHANGED
@@ -179,6 +179,7 @@ Click the type names for complete docs.
179
179
  - [`ArrayValues`](source/array-values.d.ts) - Provides all values for a constant array or tuple.
180
180
  - [`SetFieldType`](source/set-field-type.d.ts) - Create a type that changes the type of the given keys.
181
181
  - [`Paths`](source/paths.d.ts) - Generate a union of all possible paths to properties in the given object.
182
+ - [`SharedUnionFieldsDeep`](source/shared-union-fields-deep.d.ts) - Create a type with shared fields from a union of object types, deeply traversing nested structures.
182
183
 
183
184
  ### Type Guard
184
185
 
@@ -3,6 +3,7 @@ import type {ConditionalExcept} from './conditional-except';
3
3
  import type {ConditionalSimplifyDeep} from './conditional-simplify';
4
4
  import type {UnknownRecord} from './unknown-record';
5
5
  import type {EmptyObject} from './empty-object';
6
+ import type {IsPlainObject} from './internal';
6
7
 
7
8
  /**
8
9
  Used to mark properties that should be excluded.
@@ -97,7 +98,7 @@ export type ConditionalPickDeep<
97
98
  > = ConditionalSimplifyDeep<ConditionalExcept<{
98
99
  [Key in keyof Type]: AssertCondition<Type[Key], Condition, Options> extends true
99
100
  ? Type[Key]
100
- : Type[Key] extends UnknownRecord
101
+ : IsPlainObject<Type[Key]> extends true
101
102
  ? ConditionalPickDeep<Type[Key], Condition, Options>
102
103
  : typeof conditionalPickDeepSymbol;
103
104
  }, (typeof conditionalPickDeepSymbol | undefined) | EmptyObject>, never, UnknownRecord>;
@@ -1,4 +1,5 @@
1
1
  import type {DelimiterCase} from './delimiter-case';
2
+ import type {NonRecursiveType} from './internal';
2
3
  import type {UnknownArray} from './unknown-array';
3
4
 
4
5
  /**
@@ -48,7 +49,7 @@ const result: DelimiterCasedPropertiesDeep<UserWithFriends, '-'> = {
48
49
  export type DelimiterCasedPropertiesDeep<
49
50
  Value,
50
51
  Delimiter extends string,
51
- > = Value extends Function | Date | RegExp
52
+ > = Value extends NonRecursiveType
52
53
  ? Value
53
54
  : Value extends UnknownArray
54
55
  ? DelimiterCasedPropertiesArrayDeep<Value, Delimiter>
package/source/get.d.ts CHANGED
@@ -17,7 +17,7 @@ type GetOptions = {
17
17
  Like the `Get` type but receives an array of strings as a path parameter.
18
18
  */
19
19
  type GetWithPath<BaseType, Keys extends readonly string[], Options extends GetOptions = {}> =
20
- Keys extends []
20
+ Keys extends readonly []
21
21
  ? BaseType
22
22
  : Keys extends readonly [infer Head, ...infer Tail]
23
23
  ? GetWithPath<
@@ -125,7 +125,7 @@ type PropertyOf<BaseType, Key extends string, Options extends GetOptions = {}> =
125
125
  ? undefined
126
126
  : Key extends keyof BaseType
127
127
  ? StrictPropertyOf<BaseType, Key, Options>
128
- : BaseType extends [] | [unknown, ...unknown[]]
128
+ : BaseType extends readonly [] | readonly [unknown, ...unknown[]]
129
129
  ? unknown // It's a tuple, but `Key` did not extend `keyof BaseType`. So the index is out of bounds.
130
130
  : BaseType extends {
131
131
  [n: number]: infer Item;
@@ -3,6 +3,8 @@ import type {Simplify} from './simplify';
3
3
  import type {Trim} from './trim';
4
4
  import type {IsAny} from './is-any';
5
5
  import type {UnknownRecord} from './unknown-record';
6
+ import type {IsNever} from './is-never';
7
+ import type {UnknownArray} from './unknown-array';
6
8
 
7
9
  // TODO: Remove for v5.
8
10
  export type {UnknownRecord} from './unknown-record';
@@ -12,7 +14,34 @@ Infer the length of the given array `<T>`.
12
14
 
13
15
  @link https://itnext.io/implementing-arithmetic-within-typescripts-type-system-a1ef140a6f6f
14
16
  */
15
- type TupleLength<T extends readonly unknown[]> = T extends {readonly length: infer L} ? L : never;
17
+ type ArrayLength<T extends readonly unknown[]> = T extends {readonly length: infer L} ? L : never;
18
+
19
+ /**
20
+ Infer the length of the given tuple `<T>`.
21
+
22
+ Returns `never` if the given type is an non-fixed-length array like `Array<string>`.
23
+
24
+ @example
25
+ ```
26
+ type Tuple = TupleLength<[string, number, boolean]>;
27
+ //=> 3
28
+
29
+ type Array = TupleLength<string[]>;
30
+ //=> never
31
+
32
+ // Supports union types.
33
+ type Union = TupleLength<[] | [1, 2, 3] | Array<number>>;
34
+ //=> 1 | 3
35
+ ```
36
+ */
37
+ export type TupleLength<T extends UnknownArray> =
38
+ // `extends unknown` is used to convert `T` (if `T` is a union type) to
39
+ // a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types))
40
+ T extends unknown
41
+ ? number extends T['length']
42
+ ? never // Return never if the given type is an non-flexed-length array like `Array<string>`
43
+ : T['length']
44
+ : never; // Should never happen
16
45
 
17
46
  /**
18
47
  Create a tuple type of the given length `<L>` and fill it with the given type `<Fill>`.
@@ -63,19 +92,29 @@ the inferred tuple `U` and a tuple of length `B`, then extracts the length of tu
63
92
  @link https://itnext.io/implementing-arithmetic-within-typescripts-type-system-a1ef140a6f6f
64
93
  */
65
94
  export type Subtract<A extends number, B extends number> = BuildTuple<A> extends [...(infer U), ...BuildTuple<B>]
66
- ? TupleLength<U>
95
+ ? ArrayLength<U>
67
96
  : never;
68
97
 
69
98
  /**
70
- Matches any primitive, `Date`, or `RegExp` value.
99
+ Matches any primitive, `void`, `Date`, or `RegExp` value.
71
100
  */
72
- export type BuiltIns = Primitive | Date | RegExp;
101
+ export type BuiltIns = Primitive | void | Date | RegExp;
73
102
 
74
103
  /**
75
104
  Matches non-recursive types.
76
105
  */
77
106
  export type NonRecursiveType = BuiltIns | Function | (new (...args: any[]) => unknown);
78
107
 
108
+ /**
109
+ Returns a boolean for whether the given type is a plain key-value object.
110
+ */
111
+ export type IsPlainObject<T> =
112
+ T extends NonRecursiveType | UnknownArray | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
113
+ ? false
114
+ : T extends object
115
+ ? true
116
+ : false;
117
+
79
118
  export type UpperCaseCharacters = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
80
119
 
81
120
  export type StringDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
@@ -317,3 +356,118 @@ IsPrimitive<Object>
317
356
  ```
318
357
  */
319
358
  export type IsPrimitive<T> = [T] extends [Primitive] ? true : false;
359
+
360
+ /**
361
+ Returns the static, fixed-length portion of the given array, excluding variable-length parts.
362
+
363
+ @example
364
+ ```
365
+ type A = [string, number, boolean, ...string[]];
366
+ type B = StaticPartOfArray<A>;
367
+ //=> [string, number, boolean]
368
+ ```
369
+ */
370
+ export type StaticPartOfArray<T extends UnknownArray, Result extends UnknownArray = []> =
371
+ T extends unknown
372
+ ? number extends T['length'] ?
373
+ T extends readonly [infer U, ...infer V]
374
+ ? StaticPartOfArray<V, [...Result, U]>
375
+ : Result
376
+ : T
377
+ : never; // Should never happen
378
+
379
+ /**
380
+ Returns the variable, non-fixed-length portion of the given array, excluding static-length parts.
381
+
382
+ @example
383
+ ```
384
+ type A = [string, number, boolean, ...string[]];
385
+ type B = VariablePartOfArray<A>;
386
+ //=> string[]
387
+ ```
388
+ */
389
+ export type VariablePartOfArray<T extends UnknownArray> =
390
+ T extends unknown
391
+ ? T extends readonly [...StaticPartOfArray<T>, ...infer U]
392
+ ? U
393
+ : []
394
+ : never; // Should never happen
395
+
396
+ /**
397
+ Returns the minimum number in the given union of numbers.
398
+
399
+ Note: Just supports numbers from 0 to 999.
400
+
401
+ @example
402
+ ```
403
+ type A = UnionMin<3 | 1 | 2>;
404
+ //=> 1
405
+ ```
406
+ */
407
+ export type UnionMin<N extends number> = InternalUnionMin<N>;
408
+
409
+ /**
410
+ The actual implementation of `UnionMin`. It's private because it has some arguments that don't need to be exposed.
411
+ */
412
+ type InternalUnionMin<N extends number, T extends UnknownArray = []> =
413
+ T['length'] extends N
414
+ ? T['length']
415
+ : InternalUnionMin<N, [...T, unknown]>;
416
+
417
+ /**
418
+ Returns the maximum number in the given union of numbers.
419
+
420
+ Note: Just supports numbers from 0 to 999.
421
+
422
+ @example
423
+ ```
424
+ type A = UnionMax<1 | 3 | 2>;
425
+ //=> 3
426
+ ```
427
+ */
428
+ export type UnionMax<N extends number> = InternalUnionMax<N>;
429
+
430
+ /**
431
+ The actual implementation of `UnionMax`. It's private because it has some arguments that don't need to be exposed.
432
+ */
433
+ type InternalUnionMax<N extends number, T extends UnknownArray = []> =
434
+ IsNever<N> extends true
435
+ ? T['length']
436
+ : T['length'] extends N
437
+ ? InternalUnionMax<Exclude<N, T['length']>, T>
438
+ : InternalUnionMax<N, [...T, unknown]>;
439
+
440
+ /**
441
+ Returns a boolean for whether the given type is a union type.
442
+
443
+ @example
444
+ ```
445
+ type A = IsUnion<string | number>;
446
+ //=> true
447
+
448
+ type B = IsUnion<string>;
449
+ //=> false
450
+ ```
451
+ */
452
+ export type IsUnion<T> = InternalIsUnion<T>;
453
+
454
+ /**
455
+ The actual implementation of `IsUnion`.
456
+ */
457
+ type InternalIsUnion<T, U = T> =
458
+ (
459
+ // @link https://ghaiklor.github.io/type-challenges-solutions/en/medium-isunion.html
460
+ IsNever<T> extends true
461
+ ? false
462
+ : T extends any
463
+ ? [U] extends [T]
464
+ ? false
465
+ : true
466
+ : never
467
+ ) extends infer Result
468
+ // In some cases `Result` will return `false | true` which is `boolean`,
469
+ // that means `T` has at least two types and it's a union type,
470
+ // so we will return `true` instead of `boolean`.
471
+ ? boolean extends Result ? true
472
+ : Result
473
+ : never; // Should never happen
package/source/merge.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type {OmitIndexSignature} from './omit-index-signature';
2
2
  import type {PickIndexSignature} from './pick-index-signature';
3
- import type {EnforceOptional} from './enforce-optional';
3
+ import type {Simplify} from './simplify';
4
4
 
5
5
  // Merges two objects without worrying about index signatures.
6
6
  type SimpleMerge<Destination, Source> = {
@@ -41,6 +41,8 @@ export type FooBar = Merge<Foo, Bar>;
41
41
 
42
42
  @category Object
43
43
  */
44
- export type Merge<Destination, Source> = EnforceOptional<
44
+ export type Merge<Destination, Source> =
45
+ Simplify<
45
46
  SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>>
46
- & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>>;
47
+ & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>
48
+ >;
package/source/paths.d.ts CHANGED
@@ -1,41 +1,9 @@
1
- import type {NonRecursiveType, ToString} from './internal';
1
+ import type {StaticPartOfArray, VariablePartOfArray, NonRecursiveType, ToString} from './internal';
2
2
  import type {EmptyObject} from './empty-object';
3
3
  import type {IsAny} from './is-any';
4
4
  import type {IsNever} from './is-never';
5
5
  import type {UnknownArray} from './unknown-array';
6
6
 
7
- /**
8
- Return the part of the given array with a fixed index.
9
-
10
- @example
11
- ```
12
- type A = [string, number, boolean, ...string[]];
13
- type B = FilterFixedIndexArray<A>;
14
- //=> [string, number, boolean]
15
- ```
16
- */
17
- type FilterFixedIndexArray<T extends UnknownArray, Result extends UnknownArray = []> =
18
- number extends T['length'] ?
19
- T extends readonly [infer U, ...infer V]
20
- ? FilterFixedIndexArray<V, [...Result, U]>
21
- : Result
22
- : T;
23
-
24
- /**
25
- Return the part of the given array with a non-fixed index.
26
-
27
- @example
28
- ```
29
- type A = [string, number, boolean, ...string[]];
30
- type B = FilterNotFixedIndexArray<A>;
31
- //=> string[]
32
- ```
33
- */
34
- type FilterNotFixedIndexArray<T extends UnknownArray> =
35
- T extends readonly [...FilterFixedIndexArray<T>, ...infer U]
36
- ? U
37
- : [];
38
-
39
7
  /**
40
8
  Generate a union of all possible paths to properties in the given object.
41
9
 
@@ -85,8 +53,8 @@ export type Paths<T> =
85
53
  : T extends UnknownArray
86
54
  ? number extends T['length']
87
55
  // We need to handle the fixed and non-fixed index part of the array separately.
88
- ? InternalPaths<FilterFixedIndexArray<T>>
89
- | InternalPaths<Array<FilterNotFixedIndexArray<T>[number]>>
56
+ ? InternalPaths<StaticPartOfArray<T>>
57
+ | InternalPaths<Array<VariablePartOfArray<T>[number]>>
90
58
  : InternalPaths<T>
91
59
  : T extends object
92
60
  ? InternalPaths<T>
@@ -0,0 +1,195 @@
1
+ import type {NonRecursiveType, UnionMin, UnionMax, TupleLength, StaticPartOfArray, VariablePartOfArray, IsUnion} from './internal';
2
+ import type {IsNever} from './is-never';
3
+ import type {UnknownArray} from './unknown-array';
4
+
5
+ /**
6
+ Set the given array to readonly if `IsReadonly` is `true`, otherwise set the given array to normal, then return the result.
7
+
8
+ @example
9
+ ```
10
+ type ReadonlyArray = readonly string[];
11
+ type NormalArray = string[];
12
+
13
+ type ReadonlyResult = SetArrayAccess<NormalArray, true>;
14
+ //=> readonly string[]
15
+
16
+ type NormalResult = SetArrayAccess<ReadonlyArray, false>;
17
+ //=> string[]
18
+ ```
19
+ */
20
+ type SetArrayAccess<T extends UnknownArray, IsReadonly extends boolean> =
21
+ T extends readonly [...infer U] ?
22
+ IsReadonly extends true
23
+ ? readonly [...U]
24
+ : [...U]
25
+ : T;
26
+
27
+ /**
28
+ Returns whether the given array `T` is readonly.
29
+ */
30
+ type IsArrayReadonly<T extends UnknownArray> = T extends unknown[] ? false : true;
31
+
32
+ /**
33
+ SharedUnionFieldsDeep options.
34
+
35
+ @see {@link SharedUnionFieldsDeep}
36
+ */
37
+ export type SharedUnionFieldsDeepOptions = {
38
+ /**
39
+ 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.
40
+
41
+ @default false
42
+ */
43
+ recurseIntoArrays?: boolean;
44
+ };
45
+
46
+ /**
47
+ Create a type with shared fields from a union of object types, deeply traversing nested structures.
48
+
49
+ Use the {@link SharedUnionFieldsDeepOptions `Options`} to specify the behavior for arrays.
50
+
51
+ Use-cases:
52
+ - You want a safe object type where each key exists in the union object.
53
+ - You want to focus on the common fields of the union type and don't want to have to care about the other fields.
54
+
55
+ @example
56
+ ```
57
+ import type {SharedUnionFieldsDeep} from 'type-fest';
58
+
59
+ type Cat = {
60
+ info: {
61
+ name: string;
62
+ type: 'cat';
63
+ catType: string;
64
+ };
65
+ };
66
+
67
+ type Dog = {
68
+ info: {
69
+ name: string;
70
+ type: 'dog';
71
+ dogType: string;
72
+ };
73
+ };
74
+
75
+ function displayPetInfo(petInfo: (Cat | Dog)['info']) {
76
+ // typeof petInfo =>
77
+ // {
78
+ // name: string;
79
+ // type: 'cat';
80
+ // catType: string; // Needn't care about this field, because it's not a common pet info field.
81
+ // } | {
82
+ // name: string;
83
+ // type: 'dog';
84
+ // dogType: string; // Needn't care about this field, because it's not a common pet info field.
85
+ // }
86
+
87
+ // petInfo type is complex and have some needless fields
88
+
89
+ console.log('name: ', petInfo.name);
90
+ console.log('type: ', petInfo.type);
91
+ }
92
+
93
+ function displayPetInfo(petInfo: SharedUnionFieldsDeep<Cat | Dog>['info']) {
94
+ // typeof petInfo =>
95
+ // {
96
+ // name: string;
97
+ // type: 'cat' | 'dog';
98
+ // }
99
+
100
+ // petInfo type is simple and clear
101
+
102
+ console.log('name: ', petInfo.name);
103
+ console.log('type: ', petInfo.type);
104
+ }
105
+ ```
106
+
107
+ @category Object
108
+ @category Union
109
+ */
110
+ export type SharedUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions = {recurseIntoArrays: false}> =
111
+ // If `Union` is not a union type, return `Union` directly.
112
+ IsUnion<Union> extends false
113
+ ? Union
114
+ // `Union extends` will convert `Union`
115
+ // to a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
116
+ // But this is not what we want, so we need to wrap `Union` with `[]` to prevent it.
117
+ : [Union] extends [NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>]
118
+ ? Union
119
+ : [Union] extends [UnknownArray]
120
+ ? Options['recurseIntoArrays'] extends true
121
+ ? SetArrayAccess<SharedArrayUnionFieldsDeep<Union, Options>, IsArrayReadonly<Union>>
122
+ : Union
123
+ : [Union] extends [object]
124
+ ? SharedObjectUnionFieldsDeep<Union, Options>
125
+ : Union;
126
+
127
+ /**
128
+ Same as `SharedUnionFieldsDeep`, but accepts only `object`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
129
+ */
130
+ type SharedObjectUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions> =
131
+ keyof Union extends infer Keys
132
+ ? IsNever<Keys> extends false
133
+ ? {
134
+ [Key in keyof Union]:
135
+ Union[Key] extends NonRecursiveType
136
+ ? Union[Key]
137
+ : SharedUnionFieldsDeep<Union[Key], Options>
138
+ }
139
+ : {}
140
+ : Union;
141
+
142
+ /**
143
+ Same as `SharedUnionFieldsDeep`, but accepts only `UnknownArray`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
144
+ */
145
+ type SharedArrayUnionFieldsDeep<Union extends UnknownArray, Options extends SharedUnionFieldsDeepOptions> =
146
+ // Restore the readonly modifier of the array.
147
+ SetArrayAccess<
148
+ InternalSharedArrayUnionFieldsDeep<Union, Options>,
149
+ IsArrayReadonly<Union>
150
+ >;
151
+
152
+ /**
153
+ Internal helper for `SharedArrayUnionFieldsDeep`. Needn't care the `readonly` modifier of arrays.
154
+ */
155
+ type InternalSharedArrayUnionFieldsDeep<
156
+ Union extends UnknownArray,
157
+ Options extends SharedUnionFieldsDeepOptions,
158
+ ResultTuple extends UnknownArray = [],
159
+ > =
160
+ // We should build a minimum possible length tuple where each element in the tuple exists in the union tuple.
161
+ IsNever<TupleLength<Union>> extends true
162
+ // Rule 1: If all the arrays in the union have non-fixed lengths,
163
+ // like `Array<string> | [number, ...string[]]`
164
+ // we should build a tuple that is [the_fixed_parts_of_union, ...the_rest_of_union[]].
165
+ // For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, ...string[]]>`
166
+ // => `[string | number, ...string[]]`.
167
+ ? ResultTuple['length'] extends UnionMax<StaticPartOfArray<Union>['length']>
168
+ ? [
169
+ // The fixed-length part of the tuple.
170
+ ...ResultTuple,
171
+ // The rest of the union.
172
+ // Due to `ResultTuple` is the maximum possible fixed-length part of the tuple,
173
+ // so we can use `StaticPartOfArray` to get the rest of the union.
174
+ ...Array<
175
+ SharedUnionFieldsDeep<VariablePartOfArray<Union>[number], Options>
176
+ >,
177
+ ]
178
+ // Build the fixed-length tuple recursively.
179
+ : InternalSharedArrayUnionFieldsDeep<
180
+ Union, Options,
181
+ [...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
182
+ >
183
+ // Rule 2: If at least one of the arrays in the union have fixed lengths,
184
+ // like `Array<string> | [number, string]`,
185
+ // we should build a tuple of the smallest possible length to ensure any
186
+ // item in the result tuple exists in the union tuple.
187
+ // For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, string]>`
188
+ // => `[string | number, string]`.
189
+ : ResultTuple['length'] extends UnionMin<TupleLength<Union>>
190
+ ? ResultTuple
191
+ // As above, build tuple recursively.
192
+ : InternalSharedArrayUnionFieldsDeep<
193
+ Union, Options,
194
+ [...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
195
+ >;