// your answers
type Get<T, K extends string, P extends string = ''> =
K extends keyof T
? T[K]
: K extends `${infer L}.${infer R}`
? L extends keyof T
? [Get<T[L], R>] extends [never]
? Get<T, R, `${P}${P extends '' ? '' : '.'}${L}`>
: Get<T[L], R>
: P extends ''
? never
: `${P}.${L}` extends keyof T
? Get<T[`${P}.${L}`], R>
: never
: never
Solution by enochjs #34850
type Get<T, K> = K extends keyof T ? T[K] : (K extends `${infer R}.${infer rest}` ? (R extends keyof T ? Get<T[R], rest> : never) : never)
Solution by ouzexi #34222
type Get<T, K extends string> = K extends keyof T
? T[K]
: K extends `${infer L}${"."}${infer Rest}`
? L extends keyof T
? Get<T[L], Rest>
: never
: never;
Solution by Vampirelee #32617
type Get<T, K> = K extends `${infer R1}.${infer R2}`
? K extends keyof T ? T[K] : R1 extends keyof T ? Get<T[R1], R2> : never
: K extends keyof T ? T[K] : never
Solution by Heonys #32329
type Get<T, P> = P extends keyof T
? T[P]
: P extends `${infer K}.${infer R}`
? K extends keyof T
? Get<T[K], R>
: never
: never;
Solution by vangie #32303
type Get<T extends Record<string, any>, S extends string, U extends unknown[] = []> = S extends `${infer F}.${infer L}`
? F extends keyof T
? Get<T[F], L, [...U, unknown]>
: Get<T[F], L, U>
: S extends keyof T
? T[S]
: U['length'] extends 0
? never
: false;
Solution by leejaehyup #31988
type PathToArray<T extends string, R extends string[] = []> =
T extends `${infer S}.${infer L}` ?
PathToArray<L, [...R, S]> :
[...R, T]
type GetImpl<T, Path extends string[]> =
Path extends [infer F, ...infer Rest extends string[]] ?
F extends keyof T ?
GetImpl<T[F], Rest> :
never :
T
type Get<T, StringPath extends string> =
StringPath extends keyof T ?
T[StringPath] :
GetImpl<T, PathToArray<StringPath>>
type res = Get<Data, 'foo.baz'>
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Get<Data, 'hello'>, 'world'>>,
Expect<Equal<Get<Data, 'foo.bar.count'>, 6>>,
Expect<Equal<Get<Data, 'foo.bar'>, { value: 'foobar', count: 6 }>>,
Expect<Equal<Get<Data, 'foo.baz'>, false>>,
Expect<Equal<Get<Data, 'no.existed'>, never>>,
]
Solution by gearonixx #31852
type Get<T, K extends string> =
K extends `${infer Key}.${infer Rest}`
? Get<T[Key & keyof T], Rest>
: K extends keyof T ? T[K] : false;
Solution by jjswifty #31089
type Split<S extends string> = S extends `${infer First}.${infer Rest}`
? [First, ...Split<Rest>]
: [S];
type Join<S extends string[]> = S extends [infer First, ...infer Rest]
? Rest["length"] extends 0
? First
: `${First}.${Join<Rest>}`
: "";
type Get<
T,
K extends string,
StrArr extends string[] = Split<K>,
> = K extends keyof T
? T[K]
: StrArr[0] extends keyof T
? StrArr["length"] extends 1
? T[StrArr[0]]
: Get<
T[StrArr[0]],
StrArr extends [string, ...infer Rest] ? Join<Rest> : "",
StrArr extends [string, ...infer Rest] ? Rest : []
>
: never;
Solution by idebbarh #30416
type Get<T, K> = K extends `${infer A}.${infer B}` ? A extends keyof T ? Get<T[A], B> : never : K extends keyof T ? T[K] : false
Solution by tclxshunquan-wang #29764
type Get<
T,
K extends string,
Acc extends string = "",
> = K extends `${infer Current}.${infer Rest}`
? `${Acc}${Current}` extends keyof T
? [Get<T[`${Acc}${Current}`], Rest>] extends [never]
? Get<T, Rest, `${Acc}${Current}.`>
: Get<T[`${Acc}${Current}`], Rest>
: Get<T, Rest, `${Acc}${Current}.`>
: `${Acc}${K}` extends keyof T
? T[`${Acc}${K}`]
: never;
Solution by urgobalt #29521
type BaseObject = Record<string, unknown>
type Get<
Obj extends BaseObject,
Path extends string
> = Path extends keyof Obj
? Obj[Path]
: Path extends `${infer First}.${infer Other}`
? First extends keyof Obj
? Obj[First] extends BaseObject
? Get<Obj[First], Other>
: never
: never
: never
Solution by aybykovskii #28889
type Get<T, K extends string> =
K extends keyof T
? T[K]
: K extends `${infer First}.${infer Rest}`
? First extends infer F
? F extends keyof T
? Get<T[F], Rest>
: never
: never
: never
Solution by jazelly #28117
I noticed some of the answers do not cover this case: Expect<Equal<Get<Data, 'foo.baz'>, false>>
type Get<T, K> = K extends keyof T
? T[K]
: K extends `${infer S}.${infer E}`
? S extends keyof T
? Get<T[S], E>
: never
: never
Solution by furkandmrblk #27703
type Get<T, K extends string> = K extends `${infer L}.${infer R}` ? L extends keyof T ? Get<T[L], R> : never : K extends keyof T ? T[K] : never
Solution by wuxin0011 #27567
type Get<T, K> = K extends keyof T
// string key exists (works for foo.baz in tests)
? T[K]
// Else, extract the key path
: K extends `${infer A}.${infer B}`
// Recurse through
? A extends keyof T ? Get<T[A], B> : never
: K extends keyof T ? T[K] : never
Solution by HubooDeclan #27008
// your answers
type Get<T extends Object, K extends String> =
K extends keyof T
? T[K]
: K extends `${infer X}.${infer Y}`
? X extends keyof T
? T[X] extends Object
? Get<T[X], Y>
: never
: never
:never
Solution by kiki-zjq #26053
type Get<T, K extends string> = K extends keyof T ? T[K]
: K extends `${infer first}.${infer rest}` ?
first extends keyof T ?
Get<T[first], rest> : never
: never;
Solution by guckin #25776
type Get<T, K extends string> = K extends keyof T
? T[K]
: K extends `${infer KEY}.${infer R}`
? KEY extends keyof T
? Get<T[KEY], R>
: never
: never;
Solution by JohnLi1999 #25513
It also works well when the value at a certain level in the middle may be undefined or null
type Get<
T extends Record<PropertyKey, any>,
K extends string,
> = K extends keyof T
? T[K]
: K extends `${infer Key}.${infer Path}`
? undefined extends T[Key]
? T[Key] extends undefined
? undefined
: Get<Exclude<T[Key], undefined>, Path> | undefined
: null extends T[Key]
? T[Key] extends null
? null
: Get<Exclude<T[Key], null>, Path> | null
: Get<T[Key], Path>
: undefined;
// eg 1
type TestA = {
a?: {
b: 1
}
}
Get<TestA, 'a.b'> // 1 | undefined
// eg 2
type TestB = {
a: {
b: 1
} | null
}
Get<TestB, 'a.b'> // 1 | null
// eg 3
type TestC = {
a?: {
b: {
c: 1
} | null
}
}
Get<TestC, 'a.b.c'> // 1 | null | undefined
Solution by AnaniZhu #25442
// your answers
type Get<T extends {[x: PropertyKey]: any}, K extends string> = K extends keyof T
? T[K]
: K extends `${infer Left}.${infer Rest}`
? Get<T[Left], Rest>
: never
Solution by studymachiney #25181
type Get<T extends { [x: PropertyKey]: any }, K extends string> =
K extends keyof T ? T[K] : (K extends `${infer L}.${infer R}` ? Get<T[L], R> : never);
// other way
// type Get<T, K extends string> =
// T extends object ?
// K extends keyof T ? T[K] :
// K extends `${infer P}.${infer R}` ? P extends keyof T ? Get<T[P], R> : never : never :
// never;
Solution by E-uler #24766
// your answers
type Get<T, K extends string> = K extends keyof T ?T[K]:
K extends `${infer Pro}.${infer O}`
? Pro extends keyof T
? Get<T[Pro],O>
: K extends keyof T
?T[K]
:never
:never
Solution by walker-hzx #24603
type Get<T, K extends string> = K extends keyof T
? T[K]
: K extends `${infer FirstKey}.${infer SecondKey}`
? FirstKey extends keyof T
? Get<T[FirstKey], SecondKey>
: never
: never
Solution by NeylonR #24445
// your answers
type Get<T, K extends string> = K extends keyof T ? T[K] : K extends `${infer F}.${infer R}` ?
F extends keyof T ? Get<T[F], R> : never : never
Solution by snakeUni #23513
// your answers
type Get<
T extends Record<string, unknown>,
K
> = K extends keyof T
? T[K]
: K extends `${infer Key}.${infer Rest}`
? Key extends keyof T
? T[Key] extends Record<string, unknown>
? Get<T[Key], Rest>
: never
: never
: never;
Solution by jxhhdx #22855
type Split<S extends string, By extends string = '.'> = S extends ''
? []
: S extends `${infer F}${By}${infer R}`
? [F, ...Split<R, By>]
: [S]
type InferGet<T extends Record<string, any>, U extends any[] = []> =
U['length'] extends 0
? T
: U extends [infer F, ...infer R]
? F extends keyof T ? InferGet<T[F],R> : T
: T
type Get<T extends Record<string, any>,S extends string> = InferGet<T,Split<S>>
Solution by TKBnice #22852
Base Get
:
type Get<T, K extends string, TK extends keyof T = keyof T> = TK extends TK & string ? (
K extends `${TK}${infer KRest extends string}` ? (
KRest extends `.${infer KNext extends string}` ? Get<T[TK], KNext> : (
KRest extends '' ? T[TK] : never
)
) : never
) : never
With autocomplete:
type GetKeysDeep<T> = T extends Record<infer K extends string, unknown> ? (
K extends K ? K | `${K}.${GetKeysDeep<T[K]>}` : never
) : never
type GetTyped<T, K extends GetKeysDeep<T>> = Get<T, K>
Solution by BOCbMOU #22629
type Get<T extends Record<PropertyKey, any> , K extends string> =
${K}
extends keyof T
? T[K]
:${K}
extends ${infer F}${"."}${infer Rest extends string}
? ${Rest}
extends keyof T[F]
? T[F][Rest]
: ${Rest}
extends ${infer Second}${'.'}${infer Last}
? T[F][Second][Last]
: never
: never
Solution by amcguire704 #22476
type GetKey<T extends unknown[] | Record<string, any>, K extends string> = T extends unknown[]
? T[0] extends Record<string, unknown>
? T[0][K]
: never
: T extends Record<string, any>
? T[K]
: never
type Get<O extends Record<string, any>, T extends string> = unknown extends O[T]
? T extends `${infer K}.${infer R}`
? R extends string
? O[R] extends unknown
? Get<GetKey<O, K>, R>
: O[R]
: GetKey<O, K>
: T extends string
? GetKey<O, T>
: never
: O[T]
Solution by xjq7 #22367