Здесь решение, оно охватывает безопасность типов аргумента и типа возвращаемого значения:
type Unshift<A, T extends Array<any>>
= ((a: A, ...b: T) => any) extends ((...result: infer Result) => any) ? Result : never;
type Shift<T extends Array<any>>
= ((...a: T) => any) extends ((a: any, ...result: infer Result) => any) ? Result : never;
type Revert
<T extends Array<any>
, Result extends Array<any> = []
, First extends T[keyof T] = T[0]
, Rest extends Array<any> = Shift<T>> = {
[K in keyof T]: Rest['length'] extends 0 ? Unshift<First, Result> : Revert<Rest, Unshift<First, Result>>
}[0]
// this was done to avoid infinite processing the type by TS
type Level = 0 | 1 | 2 | 3 | 4 | 5
type NextLevel<X extends Level> =
X extends 0
? 1
: X extends 1
? 2
: X extends 2
? 3
: X extends 3
? 4
: X extends 4
? 5
: never
// this type will give us possible path type for the object
type RecursivePath<Obj extends object, Result extends any[] = [], Lv extends Level = 0> = {
[K in keyof Obj]:
Lv extends never
? Result
: Obj[K] extends object
? (Result['length'] extends 0 ? never : Revert<Result>) | RecursivePath<Obj[K], Unshift<K, Result>, NextLevel<Lv>>
: Revert<Result> | Revert<Unshift<K,Result>>
}[keyof Obj]
// checks if type is working
type Test = RecursivePath<{a: {b: {c: string}, d: string}}>
type Test2 = RecursivePath<{a: {b: {c: {e: string}}, d: string}}>
// this type will give as value type at given path
type RecursivePathValue<Obj, Path extends any> =
{
[K in keyof Path]:
Path extends any[]
? Path[K] extends keyof Obj
? Path['length'] extends 1
? Obj[Path[K]]
: RecursivePathValue<Obj[Path[K]], Shift<Path>>
: never
: never
}[number]
// checks if type is working
type Test3 = RecursivePathValue<{a: {b: {c: string}, d: string}},['a', 'b']>
type Test4 = RecursivePathValue<{a: {b: {c: {e: string}}, d: string}}, ['a','d']>
// finnaly the function
function recursivePluck<Obj extends object, Path extends RecursivePath<Obj>>(ob: Obj, tuple: Path): RecursivePathValue<Obj, Path> {
// inside I just fallback to any
let result: any = ob;
for (let index of tuple as any[]) {
result = result[index]
}
return result;
}
const a = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b']) // ok
const b = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','e']) // error
const c = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b','e']) // error
const d = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b','c']) // ok
const e = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b','c', 'd']) // ok
Детская площадка