type-fest 4.22.1 → 4.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "type-fest",
3
- "version": "4.22.1",
3
+ "version": "4.24.0",
4
4
  "description": "A collection of essential TypeScript types",
5
5
  "license": "(MIT OR CC0-1.0)",
6
6
  "repository": "sindresorhus/type-fest",
@@ -19,6 +19,33 @@ NumberAbsolute<NegativeInfinity>
19
19
  */
20
20
  export type NumberAbsolute<N extends number> = `${N}` extends `-${infer StringPositiveN}` ? StringToNumber<StringPositiveN> : N;
21
21
 
22
+ /**
23
+ Check whether the given type is a number or a number string.
24
+
25
+ Supports floating-point as a string.
26
+
27
+ @example
28
+ ```
29
+ type A = IsNumberLike<'1'>;
30
+ //=> true
31
+
32
+ type B = IsNumberLike<'-1.1'>;
33
+ //=> true
34
+
35
+ type C = IsNumberLike<1>;
36
+ //=> true
37
+
38
+ type D = IsNumberLike<'a'>;
39
+ //=> false
40
+ */
41
+ export type IsNumberLike<N> =
42
+ N extends number ? true
43
+ : N extends `${number}`
44
+ ? true
45
+ : N extends `${number}.${number}`
46
+ ? true
47
+ : false;
48
+
22
49
  /**
23
50
  Returns the minimum number in the given union of numbers.
24
51
 
@@ -322,7 +322,7 @@ export type MergeDeepOptions = {
322
322
 
323
323
  Note: Top-level arrays and tuples are always spread.
324
324
 
325
- @default 'spread'
325
+ @default 'replace'
326
326
  */
327
327
  arrayMergeMode?: ArrayMergeMode;
328
328
 
package/source/paths.d.ts CHANGED
@@ -1,10 +1,61 @@
1
- import type {StaticPartOfArray, VariablePartOfArray, NonRecursiveType, ToString} from './internal';
1
+ import type {StaticPartOfArray, VariablePartOfArray, NonRecursiveType, ToString, IsNumberLike} from './internal';
2
2
  import type {EmptyObject} from './empty-object';
3
3
  import type {IsAny} from './is-any';
4
- import type {IsNever} from './is-never';
5
4
  import type {UnknownArray} from './unknown-array';
6
- import type {Sum} from './sum';
7
- import type {LessThan} from './less-than';
5
+ import type {Subtract} from './subtract';
6
+ import type {GreaterThan} from './greater-than';
7
+
8
+ /**
9
+ Paths options.
10
+
11
+ @see {@link Paths}
12
+ */
13
+ export type PathsOptions = {
14
+ /**
15
+ The maximum depth to recurse when searching for paths.
16
+
17
+ @default 10
18
+ */
19
+ maxRecursionDepth?: number;
20
+
21
+ /**
22
+ Use bracket notation for array indices and numeric object keys.
23
+
24
+ @default false
25
+
26
+ @example
27
+ ```
28
+ type ArrayExample = {
29
+ array: ['foo'];
30
+ };
31
+
32
+ type A = Paths<ArrayExample, {bracketNotation: false}>;
33
+ //=> 'array' | 'array.0'
34
+
35
+ type B = Paths<ArrayExample, {bracketNotation: true}>;
36
+ //=> 'array' | 'array[0]'
37
+ ```
38
+
39
+ @example
40
+ ```
41
+ type NumberKeyExample = {
42
+ 1: ['foo'];
43
+ };
44
+
45
+ type A = Paths<NumberKeyExample, {bracketNotation: false}>;
46
+ //=> 1 | '1' | '1.0'
47
+
48
+ type B = Paths<NumberKeyExample, {bracketNotation: true}>;
49
+ //=> '[1]' | '[1][0]'
50
+ ```
51
+ */
52
+ bracketNotation?: boolean;
53
+ };
54
+
55
+ type DefaultPathsOptions = {
56
+ maxRecursionDepth: 10;
57
+ bracketNotation: false;
58
+ };
8
59
 
9
60
  /**
10
61
  Generate a union of all possible paths to properties in the given object.
@@ -47,9 +98,14 @@ open('listB.1'); // TypeError. Because listB only has one element.
47
98
  @category Object
48
99
  @category Array
49
100
  */
50
- export type Paths<T> = Paths_<T>;
101
+ export type Paths<T, Options extends PathsOptions = {}> = _Paths<T, {
102
+ // Set default maxRecursionDepth to 10
103
+ maxRecursionDepth: Options['maxRecursionDepth'] extends number ? Options['maxRecursionDepth'] : DefaultPathsOptions['maxRecursionDepth'];
104
+ // Set default bracketNotation to false
105
+ bracketNotation: Options['bracketNotation'] extends boolean ? Options['bracketNotation'] : DefaultPathsOptions['bracketNotation'];
106
+ }>;
51
107
 
52
- type Paths_<T, Depth extends number = 0> =
108
+ type _Paths<T, Options extends Required<PathsOptions>> =
53
109
  T extends NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
54
110
  ? never
55
111
  : IsAny<T> extends true
@@ -57,29 +113,58 @@ type Paths_<T, Depth extends number = 0> =
57
113
  : T extends UnknownArray
58
114
  ? number extends T['length']
59
115
  // We need to handle the fixed and non-fixed index part of the array separately.
60
- ? InternalPaths<StaticPartOfArray<T>, Depth>
61
- | InternalPaths<Array<VariablePartOfArray<T>[number]>, Depth>
62
- : InternalPaths<T, Depth>
116
+ ? InternalPaths<StaticPartOfArray<T>, Options>
117
+ | InternalPaths<Array<VariablePartOfArray<T>[number]>, Options>
118
+ : InternalPaths<T, Options>
63
119
  : T extends object
64
- ? InternalPaths<T, Depth>
120
+ ? InternalPaths<T, Options>
65
121
  : never;
66
122
 
67
- export type InternalPaths<_T, Depth extends number = 0, T = Required<_T>> =
68
- T extends EmptyObject | readonly []
69
- ? never
70
- : {
71
- [Key in keyof T]:
72
- Key extends string | number // Limit `Key` to string or number.
73
- // If `Key` is a number, return `Key | `${Key}``, because both `array[0]` and `array['0']` work.
74
- ?
75
- | Key
76
- | ToString<Key>
77
- | (
78
- LessThan<Depth, 15> extends true // Limit the depth to prevent infinite recursion
79
- ? IsNever<Paths_<T[Key], Sum<Depth, 1>>> extends false
80
- ? `${Key}.${Paths_<T[Key], Sum<Depth, 1>>}`
123
+ type InternalPaths<T, Options extends Required<PathsOptions>> =
124
+ Options['maxRecursionDepth'] extends infer MaxDepth extends number
125
+ ? Required<T> extends infer T
126
+ ? T extends EmptyObject | readonly []
127
+ ? never
128
+ : {
129
+ [Key in keyof T]:
130
+ Key extends string | number // Limit `Key` to string or number.
131
+ ? (
132
+ Options['bracketNotation'] extends true
133
+ ? IsNumberLike<Key> extends true
134
+ ? `[${Key}]`
135
+ : (Key | ToString<Key>)
136
+ : never
137
+ |
138
+ Options['bracketNotation'] extends false
139
+ // If `Key` is a number, return `Key | `${Key}``, because both `array[0]` and `array['0']` work.
140
+ ? (Key | ToString<Key>)
141
+ : never
142
+ ) extends infer TranformedKey extends string | number ?
143
+ // 1. If style is 'a[0].b' and 'Key' is a numberlike value like 3 or '3', transform 'Key' to `[${Key}]`, else to `${Key}` | Key
144
+ // 2. If style is 'a.0.b', transform 'Key' to `${Key}` | Key
145
+ | TranformedKey
146
+ | (
147
+ // Recursively generate paths for the current key
148
+ GreaterThan<MaxDepth, 0> extends true // Limit the depth to prevent infinite recursion
149
+ ? _Paths<T[Key], {bracketNotation: Options['bracketNotation']; maxRecursionDepth: Subtract<MaxDepth, 1>}> extends infer SubPath
150
+ ? SubPath extends string | number
151
+ ? (
152
+ Options['bracketNotation'] extends true
153
+ ? SubPath extends `[${any}]` | `[${any}]${string}`
154
+ ? `${TranformedKey}${SubPath}` // If next node is number key like `[3]`, no need to add `.` before it.
155
+ : `${TranformedKey}.${SubPath}`
156
+ : never
157
+ ) | (
158
+ Options['bracketNotation'] extends false
159
+ ? `${TranformedKey}.${SubPath}`
160
+ : never
161
+ )
162
+ : never
163
+ : never
164
+ : never
165
+ )
81
166
  : never
82
167
  : never
83
- )
84
- : never
85
- }[keyof T & (T extends UnknownArray ? number : unknown)];
168
+ }[keyof T & (T extends UnknownArray ? number : unknown)]
169
+ : never
170
+ : never;