@@ -72,37 +72,64 @@ type AnyIsEqual<T1, T2> = T1 extends T2 ? (IsEqual<T1, T2> extends true ? true :
7272type TupleKeys < T extends ReadonlyArray < any > > = Exclude < keyof T , keyof any [ ] > ;
7373
7474/**
75- * Helper type for recursively constructing paths through a type.
76- * This actually constructs the strings and recurses into nested
77- * object types.
75+ * Helper type to construct tuple key paths and recurse into its elements.
76+ *
77+ * See {@link Path}
78+ */
79+ type PathInternalTuple < TValue extends ReadonlyArray < any > , TraversedTypes > = {
80+ [ Key in TupleKeys < TValue > & string ] : `[${Key } ]` | `[${Key } ]${PathInternal < TValue [ Key ] , TraversedTypes , false > } `;
81+ } [ TupleKeys < TValue > & string ] ;
82+
83+ /**
84+ * Helper type to construct array key paths and recurse into its elements.
85+ *
86+ * See {@link Path}
87+ */
88+ type PathInternalArray < TValue extends ReadonlyArray < any > , TraversedTypes > =
89+ | `[${ArrayKey } ]`
90+ | `[${ArrayKey } ]${PathInternal < TValue [ ArrayKey ] , TraversedTypes , false > } `;
91+
92+ /**
93+ * Helper type to construct object key paths and recurse into its nested values.
94+ *
95+ * See {@link Path}
96+ */
97+ type PathInternalObject < TValue , TraversedTypes , First extends boolean > = {
98+ [ Key in keyof TValue & string ] : First extends true
99+ ? `${Key } ` | `${Key } ${PathInternal < TValue [ Key ] , TraversedTypes , false > } `
100+ : `.${Key } ` | `.${Key } ${PathInternal < TValue [ Key ] , TraversedTypes , false > } `;
101+ } [ keyof TValue & string ] ;
102+
103+ /**
104+ * Helper type to construct nested any object key paths.
78105 *
79106 * See {@link Path}
80107 */
81- type PathImpl < K extends string | number , V , TraversedTypes > = V extends Primitive | BrowserNativeObject
82- ? `${K } `
83- : // Check so that we don't recurse into the same type
84- // by ensuring that the types are mutually assignable
85- // mutually required to avoid false positives of subtypes
86- true extends AnyIsEqual < TraversedTypes , V >
87- ? `${K } `
88- : `${K } ` | `${K } .${PathInternal < V , TraversedTypes | V > } `;
108+ type PathInternalAny = `.${string } ` | `[${string } ]` | `[${string } ].${string } `;
89109
90110/**
91111 * Helper type for recursively constructing paths through a type.
92- * This obscures the internal type param TraversedTypes from ed contract.
112+ *
113+ * This obscures internal type params TraversedTypes and First from ed contract.
93114 *
94115 * See {@link Path}
95116 */
96- type PathInternal < T , TraversedTypes = T > =
97- T extends ReadonlyArray < infer V >
98- ? IsTuple < T > extends true
99- ? {
100- [ K in TupleKeys < T > ] -?: PathImpl < K & string , T [ K ] , TraversedTypes > ;
101- } [ TupleKeys < T > ]
102- : PathImpl < ArrayKey , V , TraversedTypes >
103- : {
104- [ K in keyof T ] -?: PathImpl < K & string , T [ K ] , TraversedTypes > ;
105- } [ keyof T ] ;
117+ type PathInternal < TValue , TraversedTypes , First extends boolean > = TValue extends Primitive | BrowserNativeObject
118+ ? IsAny < TValue > extends true
119+ ? PathInternalAny
120+ : never
121+ : TValue extends ReadonlyArray < any >
122+ ? // Check so that we don't recurse into the same type by ensuring that the
123+ // types are mutually assignable mutually required to avoid false
124+ // positives of subtypes
125+ true extends AnyIsEqual < TraversedTypes , TValue >
126+ ? never
127+ : IsTuple < TValue > extends true
128+ ? PathInternalTuple < TValue , TraversedTypes | TValue >
129+ : PathInternalArray < TValue , TraversedTypes | TValue >
130+ : TValue extends Record < string , any >
131+ ? PathInternalObject < TValue , TraversedTypes | TValue , First >
132+ : '' ;
106133
107134/**
108135 * Helper type for recursively constructing paths through a type.
@@ -200,4 +227,4 @@ export type PathValue<T, P extends Path<T> | ArrayPath<T>> = T extends any
200227 */
201228// We want to explode the union type and process each individually
202229// so assignable types don't leak onto the stack from the base.
203- export type Path < T > = T extends any ? PathInternal < T > : never ;
230+ export type Path < T > = T extends any ? PathInternal < T , T , true > & string : never ;
0 commit comments