This question is an extension of the one found here.
I have an object:
type exampleType = {
propertyOne: string
propertyTwo: number,
propertyThree: {
propertyFour: string,
propertyFive: Date,
propertySix: boolean,
}
}
I'm looking for a type that would validate a dot-notation like string to a path of either string
or Date
. In the example above, this would mean it the type compiles to:
propertyOne | propertyThree.propertyFour | propertyThree.PropertyFive
Using the question previously asked above, the following is possible:
type PathsToStringProps<T> = T extends string ? [] : {
[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>]
}[Extract<keyof T, string>];
type Join<T extends string[], D extends string> =
T extends [] ? never :
T extends [infer F] ? F :
T extends [infer F, ...infer R] ?
F extends string ?
`${F}${D}${Join<Extract<R, string[]>, D>}` : never : string;
type Path = Join<PathsToStringProps<exampleType>, ".">
I'm trying to make the above solution generic, so that I could give Path
two generic arguments: T
, which would represent exampleType
here, and V
, which would be string|Date
in my example above.
When I tried making exampleType
generic:
type Path<T> = Join<PathsToStringProps<T>, ".">
I got this error: Excessive stack depth comparing types 'PathsToStringProps<T>' and 'string[]'.ts(2321)
Which I was able to solve by specifying that T must represent a Key-Value object:
type Path<T extends {[key: string]: any}> = Join<PathsToStringProps<T>, ".">
Moving on to restricting the type of value to path points to:
type PathsToStringProps<T, V> = T extends (V) ? [] : {
[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K], V>]
}[Extract<keyof T, string>];
type Join<T extends string[], D extends string> =
T extends [] ? never :
T extends [infer F] ? F :
T extends [infer F, ...infer R] ?
F extends string ?
`${F}${D}${Join<Extract<R, string[]>, D>}` : never : string;
type Path<T extends {[key: string]: any}, V> = Join<PathsToStringProps<T, V>, ".">
But I get an error:
Which disappears if I remove the generic argument V from Path
, but keep it in PathsToStringProps
:
type Path<T extends {[key: string]: any}> = Join<PathsToStringProps<T, string|Date>, ".">
Here's a TypeScript Playground of my final attempt at getting this to work.