|
1 | | -import type {NonRecursiveType, ToString, IsNumberLike, ApplyDefaultOptions} from './internal/index.d.ts'; |
| 1 | +import type {NonRecursiveType, ToString, IsNumberLike, ApplyDefaultOptions, MapsSetsOrArrays} from './internal/index.d.ts'; |
2 | 2 | import type {IsAny} from './is-any.d.ts'; |
3 | 3 | import type {UnknownArray} from './unknown-array.d.ts'; |
4 | 4 | import type {GreaterThan} from './greater-than.d.ts'; |
@@ -192,62 +192,48 @@ open('listB.1'); // TypeError. Because listB only has one element. |
192 | 192 | export type Paths<T, Options extends PathsOptions = {}> = _Paths<T, ApplyDefaultOptions<PathsOptions, DefaultPathsOptions, Options>>; |
193 | 193 |
|
194 | 194 | type _Paths<T, Options extends Required<PathsOptions>, CurrentDepth extends number = 0> = |
195 | | - T extends NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> |
| 195 | + T extends NonRecursiveType | Exclude<MapsSetsOrArrays, UnknownArray> |
196 | 196 | ? never |
197 | 197 | : IsAny<T> extends true |
198 | 198 | ? never |
199 | 199 | : T extends object |
200 | | - ? InternalPaths<T, Options, CurrentDepth> |
| 200 | + ? InternalPaths<Required<T>, Options, CurrentDepth> |
201 | 201 | : never; |
202 | 202 |
|
203 | 203 | type InternalPaths<T, Options extends Required<PathsOptions>, CurrentDepth extends number> = |
204 | | - Options['maxRecursionDepth'] extends infer MaxDepth extends number |
205 | | - ? Required<T> extends infer T |
206 | | - ? T extends readonly [] |
207 | | - ? never |
208 | | - : IsNever<keyof T> extends true // Check for empty object |
209 | | - ? never |
210 | | - : { |
211 | | - [Key in keyof T]: |
212 | | - Key extends string | number // Limit `Key` to string or number. |
213 | | - ? ( |
214 | | - And<Options['bracketNotation'], IsNumberLike<Key>> extends true |
215 | | - ? `[${Key}]` |
216 | | - // If `Key` is a number, return `Key | `${Key}``, because both `array[0]` and `array['0']` work. |
217 | | - : CurrentDepth extends 0 |
218 | | - ? Key | ToString<Key> |
219 | | - : `.${(Key | ToString<Key>)}` |
220 | | - ) extends infer TranformedKey extends string | number ? |
221 | | - // 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 |
222 | | - // 2. If style is 'a.0.b', transform 'Key' to `${Key}` | Key |
223 | | - | ((Options['leavesOnly'] extends true |
224 | | - ? MaxDepth extends CurrentDepth |
225 | | - ? TranformedKey |
226 | | - : T[Key] extends infer Value |
227 | | - ? (Value extends readonly [] | NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> |
228 | | - ? TranformedKey |
229 | | - : IsNever<keyof Value> extends true // Check for empty object |
230 | | - ? TranformedKey |
231 | | - : never) |
232 | | - : never |
233 | | - : TranformedKey |
234 | | - ) extends infer _TransformedKey |
235 | | - // If `depth` is provided, the condition becomes truthy only when it reaches `CurrentDepth`. |
236 | | - // Otherwise, since `depth` defaults to `number`, the condition is always truthy, returning paths at all depths. |
237 | | - ? CurrentDepth extends Options['depth'] |
238 | | - ? _TransformedKey |
239 | | - : never |
| 204 | + {[Key in keyof T]: Key extends string | number // Limit `Key` to `string | number` |
| 205 | + ? ( |
| 206 | + And<Options['bracketNotation'], IsNumberLike<Key>> extends true |
| 207 | + ? `[${Key}]` |
| 208 | + : CurrentDepth extends 0 |
| 209 | + // Return both `Key` and `ToString<Key>` because for number keys, like `1`, both `1` and `'1'` are valid keys. |
| 210 | + ? Key | ToString<Key> |
| 211 | + : `.${(Key | ToString<Key>)}` |
| 212 | + ) extends infer TransformedKey extends string | number |
| 213 | + ? ((Options['leavesOnly'] extends true |
| 214 | + ? Options['maxRecursionDepth'] extends CurrentDepth |
| 215 | + ? TransformedKey |
| 216 | + : T[Key] extends infer Value // For distributing `T[Key]` |
| 217 | + ? (Value extends readonly [] | NonRecursiveType | Exclude<MapsSetsOrArrays, UnknownArray> |
| 218 | + ? TransformedKey |
| 219 | + : IsNever<keyof Value> extends true // Check for empty object & `unknown`, because `keyof unknown` is `never`. |
| 220 | + ? TransformedKey |
240 | 221 | : never) |
241 | | - | ( |
242 | | - // Recursively generate paths for the current key |
243 | | - GreaterThan<MaxDepth, CurrentDepth> extends true // Limit the depth to prevent infinite recursion |
244 | | - ? `${TranformedKey}${_Paths<T[Key], Options, Sum<CurrentDepth, 1>> & (string | number)}` |
245 | | - : never |
246 | | - ) |
247 | | - : never |
248 | | - : never |
249 | | - }[keyof T & (T extends UnknownArray ? number : unknown)] |
| 222 | + : never // Should never happen |
| 223 | + : TransformedKey |
| 224 | + ) extends infer _TransformedKey |
| 225 | + // If `depth` is provided, the condition becomes truthy only when it matches `CurrentDepth`. |
| 226 | + // Otherwise, since `depth` defaults to `number`, the condition is always truthy, returning paths at all depths. |
| 227 | + ? CurrentDepth extends Options['depth'] |
| 228 | + ? _TransformedKey |
| 229 | + : never |
| 230 | + : never) |
| 231 | + // Recursively generate paths for the current key |
| 232 | + | (GreaterThan<Options['maxRecursionDepth'], CurrentDepth> extends true // Limit the depth to prevent infinite recursion |
| 233 | + ? `${TransformedKey}${_Paths<T[Key], Options, Sum<CurrentDepth, 1>> & (string | number)}` |
| 234 | + : never) |
250 | 235 | : never |
251 | | - : never; |
| 236 | + : never |
| 237 | + }[keyof T & (T extends UnknownArray ? number : unknown)]; |
252 | 238 |
|
253 | 239 | export {}; |
0 commit comments