11import type { StaticPartOfArray , VariablePartOfArray , NonRecursiveType , ToString , IsNumberLike , ApplyDefaultOptions } from './internal/index.d.ts' ;
22import type { IsAny } from './is-any.d.ts' ;
33import type { UnknownArray } from './unknown-array.d.ts' ;
4- import type { Subtract } from './subtract.d.ts' ;
54import type { GreaterThan } from './greater-than.d.ts' ;
65import type { IsNever } from './is-never.d.ts' ;
6+ import type { Sum } from './sum.d.ts' ;
7+ import type { And } from './and.d.ts' ;
78
89/**
910Paths options.
@@ -190,21 +191,23 @@ open('listB.1'); // TypeError. Because listB only has one element.
190191*/
191192export type Paths < T , Options extends PathsOptions = { } > = _Paths < T , ApplyDefaultOptions < PathsOptions , DefaultPathsOptions , Options > > ;
192193
193- type _Paths < T , Options extends Required < PathsOptions > > =
194+ type _Paths < T , Options extends Required < PathsOptions > , CurrentDepth extends number = 0 > =
194195 T extends NonRecursiveType | ReadonlyMap < unknown , unknown > | ReadonlySet < unknown >
195196 ? never
196197 : IsAny < T > extends true
197198 ? never
198199 : T extends UnknownArray
199200 ? number extends T [ 'length' ]
200201 // We need to handle the fixed and non-fixed index part of the array separately.
201- ? InternalPaths < StaticPartOfArray < T > , Options > | InternalPaths < Array < VariablePartOfArray < T > [ number ] > , Options >
202- : InternalPaths < T , Options >
202+ ?
203+ | InternalPaths < StaticPartOfArray < T > , Options , CurrentDepth >
204+ | InternalPaths < Array < VariablePartOfArray < T > [ number ] > , Options , CurrentDepth >
205+ : InternalPaths < T , Options , CurrentDepth >
203206 : T extends object
204- ? InternalPaths < T , Options >
207+ ? InternalPaths < T , Options , CurrentDepth >
205208 : never ;
206209
207- type InternalPaths < T , Options extends Required < PathsOptions > > =
210+ type InternalPaths < T , Options extends Required < PathsOptions > , CurrentDepth extends number > =
208211 Options [ 'maxRecursionDepth' ] extends infer MaxDepth extends number
209212 ? Required < T > extends infer T
210213 ? T extends readonly [ ]
@@ -215,19 +218,17 @@ type InternalPaths<T, Options extends Required<PathsOptions>> =
215218 [ Key in keyof T ] :
216219 Key extends string | number // Limit `Key` to string or number.
217220 ? (
218- Options [ 'bracketNotation' ] extends true
219- ? IsNumberLike < Key > extends true
220- ? `[${Key } ]`
221- : ( Key | ToString < Key > )
222- : Options [ 'bracketNotation' ] extends false
221+ And < Options [ 'bracketNotation' ] , IsNumberLike < Key > > extends true
222+ ? `[${Key } ]`
223223 // If `Key` is a number, return `Key | `${Key}``, because both `array[0]` and `array['0']` work.
224- ? ( Key | ToString < Key > )
225- : never
224+ : CurrentDepth extends 0
225+ ? Key | ToString < Key >
226+ : `.${( Key | ToString < Key > ) } `
226227 ) extends infer TranformedKey extends string | number ?
227228 // 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
228229 // 2. If style is 'a.0.b', transform 'Key' to `${Key}` | Key
229230 | ( ( Options [ 'leavesOnly' ] extends true
230- ? MaxDepth extends 0
231+ ? MaxDepth extends CurrentDepth
231232 ? TranformedKey
232233 : T [ Key ] extends infer Value
233234 ? ( Value extends readonly [ ] | NonRecursiveType | ReadonlyMap < unknown , unknown > | ReadonlySet < unknown >
@@ -238,36 +239,16 @@ type InternalPaths<T, Options extends Required<PathsOptions>> =
238239 : never
239240 : TranformedKey
240241 ) extends infer _TransformedKey
241- // If `depth` is provided, the condition becomes truthy only when it reaches `0 `.
242+ // If `depth` is provided, the condition becomes truthy only when it reaches `CurrentDepth `.
242243 // Otherwise, since `depth` defaults to `number`, the condition is always truthy, returning paths at all depths.
243- ? 0 extends Options [ 'depth' ]
244+ ? CurrentDepth extends Options [ 'depth' ]
244245 ? _TransformedKey
245246 : never
246247 : never )
247248 | (
248249 // Recursively generate paths for the current key
249- GreaterThan < MaxDepth , 0 > extends true // Limit the depth to prevent infinite recursion
250- ? _Paths < T [ Key ] ,
251- {
252- bracketNotation : Options [ 'bracketNotation' ] ;
253- maxRecursionDepth : Subtract < MaxDepth , 1 > ;
254- leavesOnly : Options [ 'leavesOnly' ] ;
255- depth : Subtract < Options [ 'depth' ] , 1 > ;
256- } > extends infer SubPath
257- ? SubPath extends string | number
258- ? (
259- Options [ 'bracketNotation' ] extends true
260- ? SubPath extends `[${any } ]` | `[${any } ]${string } `
261- ? `${TranformedKey } ${SubPath } ` // If next node is number key like `[3]`, no need to add `.` before it.
262- : `${TranformedKey } .${SubPath } `
263- : never
264- ) | (
265- Options [ 'bracketNotation' ] extends false
266- ? `${TranformedKey } .${SubPath } `
267- : never
268- )
269- : never
270- : never
250+ GreaterThan < MaxDepth , CurrentDepth > extends true // Limit the depth to prevent infinite recursion
251+ ? `${TranformedKey } ${_Paths < T [ Key ] , Options , Sum < CurrentDepth , 1 > > & ( string | number ) } `
271252 : never
272253 )
273254 : never
0 commit comments