00270-hard-typed-get

Back

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 gearonix #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 aabrupt #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

// your answers
type GetPath<T extends string, R extends string[] = [], curKey extends string = ''> = T extends `${infer First}${infer Rest}` ? First extends  '.' ? GetPath<Rest, [...R, curKey]>  : GetPath<Rest, R, `${curKey}${First}`>  : curKey extends '' ? R : [...R, curKey]

type Get<T extends Record<string, any>, K extends string | string[], Deep extends 1[] = []> = 
K extends string 
  ? Get<T, GetPath<K>> 
  : K extends [infer First, ...infer Rest extends string[]] 
    ? First extends keyof T 
      ? Get<T[First], Rest, [1]> 
      : [] extends Deep ? never : false
    : T

Solution by 437204933 #22294