00956-hard-deeppick

Back

type DeepPickNoUnion<T extends object, S> = S extends `${infer K}.${infer Rest}`
  ? K extends keyof T
    ? Record<K, DeepPickNoUnion<T[K] & object, Rest>>
    : unknown
  : S extends keyof T
  ? Record<S, T[S]>
  : unknown;

type DeepPick<T extends object, S extends string> = (
  S extends any ? (a: DeepPickNoUnion<T, S>) => void : never
) extends (a: infer R) => void
  ? R
  : unknown;

Solution by Vampirelee #32628

type SingleDeepPick<T, P> = P extends `${infer K}.${infer R}`
  ? K extends keyof T
    ? { [k in K]: SingleDeepPick<T[K], R> }
    : unknown
  : P extends keyof T
  ? { [k in P]: T[P] }
  : unknown;

type DeepPick<T, P> = (
  P extends unknown ? (k: SingleDeepPick<T, P>) => void : never
) extends (k: infer I) => void
  ? I
  : never;

Solution by vangie #32302


type UnionToIntersection<T> = (T extends T ? (arg: T) => unknown : never) extends 
  (arg: infer S) => unknown ? S : never

type DeepPickImpl<T extends unknown, U extends string> = {
  0: U extends `${infer S}.${infer Rest extends string}`
    ? S extends keyof T
      ? { [K in S]: DeepPickImpl<T[S], Rest> }
      : unknown
    : [U] extends [keyof T]
      ? { [K in U]: T[K] }
      : unknown
  1: never
}[U extends U ? 0 : 1]

type DeepPick<T extends unknown, U extends string> = UnionToIntersection<DeepPickImpl<T, U>>

/* _____________ Test Cases _____________ */
import type { Equal }           from '@type-challenges/utils'
import type { Expect }          from '@type-challenges/utils'

type Obj = {
  a: number
  b: string
  c: boolean
  obj: {
    d: number
    e: string
    f: boolean
    obj2: {
      g: number
      h: string
      i: boolean
    }
  }
  obj3: {
    j: number
    k: string
    l: boolean
  }
}

type cases = [
  Expect<Equal<DeepPick<Obj, ''>, unknown>>,
  Expect<Equal<DeepPick<Obj, 'a'>, { a: number }>>,
  // Expect<Equal<DeepPick<Obj, 'a' | ''>, { a: number } & unknown>>,
  Expect<Equal<DeepPick<Obj, 'a' | 'obj.e'>, { a: number } & { obj: { e: string } }>>,
  Expect<
    Equal<
      DeepPick<Obj, 'a' | 'obj.e' | 'obj.obj2.i'>,
      { a: number } & { obj: { e: string } } & { obj: { obj2: { i: boolean } } }
    >
  >,
]

/* _____________ Further Steps _____________ */
/*
  > Share your solutions: https://tsch.js.org/956/answer
  > View solutions: https://tsch.js.org/956/solutions
  > More Challenges: https://tsch.js.org
*/

Solution by gearonix #31887

type UnionToIntersection<T> = (T extends any ? (args: T) => any : never) extends (args: infer R) => any ? R : never;

type LastInUnion<T> = UnionToIntersection<(T extends any ? (arg: T) => any : never)> extends (arg: infer R) => any ? R : never;

type UnionToTuple<T, U = T> = [T] extends [never] ? [] : [LastInUnion<T>, ...UnionToTuple<Exclude<U, LastInUnion<T>>>];

type PickOne<T, K, Last = T> = K extends '' ? unknown : K extends `${infer A extends string}.${infer B}` ? (A extends keyof T ? {
  [key in A]: PickOne<T[key], B, T[key]>
} : never) : K extends keyof Last ? {
  [K1 in K]: Last[K1]
} : Last

type Tuple<T> = UnionToTuple<T>

type PickLoop<T, K extends any[], U = unknown> = K extends [infer A, ...infer B] ? PickLoop<T, B, PickOne<T, A> & U> : U

type DeepPick<T, K> = PickLoop<T, Tuple<K>>

Solution by tclxshunquan-wang #29843

// your answers
type UnionToEx<T> = (T extends T ? (x: T) => void : never) extends (x: infer R) => void ? R : never

type FindObj<Obj, Path extends unknown[]> = 
Obj extends object 
  ?  Path extends [infer Head, ...infer Rest] 
     ? Head extends keyof Obj 
        ?
         {
          [Key in keyof Obj as Head]: FindObj<Obj[Head], Rest>
        }
        : never
      :unknown
  : Obj

type GetPath<SPath extends string> = SPath extends `${infer Key}.${infer Rest}` ? [Key, ...GetPath<Rest>] : [SPath]

type DeepPick<Obj extends object, SPath extends string> = UnionToEx<FindObj<Obj, GetPath<SPath>>

Solution by 437204933 #29698

// your answers
type Get<T, K> = K extends keyof T ? Record<K, T[K]> : K extends `${infer F}.${infer Rest}` ? F extends keyof T ? Record<F, Get<T[F], Rest>> : never : never
type GetUnion<T, K> = K extends any ? Get<T, K> : never
type UnionToIntercetion<T> = (T extends any ? (a: T) => any : never) extends (a: infer F) => any ? F : never
type DeepPick<T, K> = UnionToIntercetion<GetUnion<T, K>>

Solution by AAA611 #29201

// your answers
type Deep = Record<string, any>
type GetValueFromObjByString<T extends Deep, key extends string> = key extends `${infer K}.${infer R}` ? T[K] extends Deep ? 
    {[k in K] :GetValueFromObjByString<T[K], R>} : unknown : key extends '' ? unknown : {[k in key]:T[key]}
type UnionToIntercetion<U> = (U extends any ? (arg: U) => any : never) extends ((arg: infer I) => any) ? I : never
type DeepPick<T extends Deep, key extends string,> =
    UnionToIntercetion<key extends infer Keys extends string ? GetValueFromObjByString<T, Keys> : never>

Solution by WangZiChu199910252255 #29099

type TypeGet<T, Paths> = Paths extends `${infer A}.${infer B}`
  // If it needs to recursively go through because there's a .
  ? A extends keyof T
    ? { [K in A]: TypeGet<T[A], B> }
    : never
  // If the path is just a single key and is a key of T
  : Paths extends keyof T
    ? { [K in Paths]: T[Paths] }
    : never

// Construct the result from a | b to a & b etc
type UnionToIntercetion<U> = (U extends any ? (arg: U) => any : never) extends ((arg: infer I) => any) ? I : never

type DeepPick<T, PathUnion extends string> = UnionToIntercetion<TypeGet<T, PathUnion>>

Solution by dec-land #27237

type UnionToIntersection<T> = (T extends T ? (arg: T) => any : never) extends (arg: infer P) => any ? P : never;

type DeepPick<T extends { [x: PropertyKey]: any }, U extends string> = UnionToIntersection<
  U extends keyof T ? Pick<T, U> :
  (U extends `${infer L}.${infer R}` ?
    { [P in L]: DeepPick<T[L], R> } :
    never)  // UnionToIntersection<never> ==> unknown
>;

// other way

// type UnionToIntersection<T> = (T extends any ? (arg: T) => void : never) extends (arg: infer P) => void ? P : never;
// type UnionLast<T> = UnionToIntersection<T extends any ? () => T : never> extends () => infer R ? R : never;
// type DeepPickecursive<T extends object, U extends string> =
//   U extends `${infer K}.${infer R}` ? K extends keyof T ? T[K] extends object ? { [P in K]: DeepPickecursive<T[K], R> } : unknown : unknown :
//   U extends keyof T ? { [P in U]: T[U] } : unknown;

// type DeepPick<T extends object, U extends string, V = UnionLast<U>> = [U] extends [never] ? unknown :
//   DeepPick<T, Exclude<U, V>> & DeepPickecursive<T, V extends string ? V : never>;

Solution by E-uler #24951

type UnionToIntersection<U> = 
  (U extends U ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I : never

type DeepPick<T, K extends string> = UnionToIntersection<DeepPickUnion<T, K>>

type DeepPickUnion<T, K extends string> =
  K extends `${infer KFirst extends keyof T & string}.${infer KRest extends string}`
    ? { [Key in KFirst]: DeepPickUnion<T[Key], KRest> }
    : K extends keyof T ? { [Key in K]: T[Key] } : never

Solution by BOCbMOU #24489

// your answers
type PickDeep<T, K> = K extends keyof T ? Pick<T, K> : 
K extends `${infer Key}.${infer R}`
  ? Key extends keyof T
    ? { [P in keyof T as P extends Key ? P : never]: PickDeep<T[Key], R> }
    : never
    : never

 type UnionToIntersection<T> =(T extends unknown ? (arg: T) => void : never) extends (arg: infer Arg) => void
   ? Arg
   : never


type DeepPick<T, K> = UnionToIntersection<PickDeep<T, K>> 

Solution by snakeUni #23739

type Split<T extends string, By extends string = '.'> = 
T extends `${infer F}${By}${infer R}`
? [F,...Split<R,By>]
: [T]


type PickByPath<T, U extends any[] > = 
T extends object
? U extends [infer F, ...infer R]
? F extends keyof T 
? { [P in F]: PickByPath<T[P],R> }
: never
: T
: T

type UnionToIntersection<U> = (U extends unknown ? (arg: U) => 0 : never) extends (
	arg: infer I
) => 0
	? I
	: never


type DeepPick<T extends object, U extends string > = UnionToIntersection<PickByPath<T, Split<U>>>

Solution by TKBnice #23317

type Split<T extends string, By extends string = '.'> = 
T extends `${infer F}${By}${infer R}`
? [F,...Split<R,By>]
: [T]


type ArrayDeepPick2<T, U extends any[] > = 
T extends object
? U extends [infer F, ...infer R]
? F extends keyof T 
? { [P in F]: ArrayDeepPick2<T[P],R> }
: never
: never
: T

type UnionToIntersection<U> = (U extends unknown ? (arg: U) => 0 : never) extends (
	arg: infer I
) => 0
	? I
	: never


type DeepPick<T extends object, U extends string > = UnionToIntersection<ArrayDeepPick2<T, Split<U>>>

Solution by TKBnice #23316

type GetObject<T extends Record<PropertyKey, any>, S extends string> =
  S extends keyof T
    ? {
      [Key in S]: T[Key]
    }
    : S extends `${infer A}.${infer Rest}`
      ? {
        [Key in A]: Rest extends [] ? T[A] : GetObject<T[A], Rest>
      }
      : never

type UnionToIntersection<U> =
  (U extends U
    ? (arg: U) => void
    : never
  ) extends (arg: infer R) => void
    ? R
    : never

type DeepPick<T extends Record<string, any>, U extends string> =
  UnionToIntersection<U extends U ? GetObject<T, U> : never>

Solution by drylint #22344

type TokenizePath<P extends string, T extends string[] = [], TT extends string = ''> = 
  P extends ''
    ? TT extends ''
      ? T
      : [...T, TT]
    : P extends `${infer First}${infer Rest}`
      ? First extends '.'
        ? TT extends ''
          ? TokenizePath<Rest, T>
          : TokenizePath<Rest, [...T, TT]>
        : TokenizePath<Rest, T, `${TT}${First}`>
      : never

type PickFieldByPathTokens<O extends Record<string, any>, T extends string[]> = 
  T extends [infer FirstToken extends string, ...infer RestTokens extends string[]]
    ? {[K in FirstToken]: PickFieldByPathTokens<O[K], RestTokens>}
    : O

type PickField<O extends Record<PropertyKey, any>, P extends string> = 
  P extends ''
    ? unknown
    : PickFieldByPathTokens<O, TokenizePath<P>>

type Wrap<T> = [T]
type Unwrap<T> = T extends [infer U] ? U : T

type Union2Intersection<U> = 
  (U extends unknown ? (a: Unwrap<U>) => void : never) extends (a: infer I) => void ? I : never

type DeepPick<O extends Record<PropertyKey, any>, P extends string> = Union2Intersection<P extends unknown ? Wrap<PickField<O, P>> : never>

playground

The main idea is, for each path, do deep-pick and build an object with only one field. with distributive conditional types, the result is an union, so we then have to turn union into intersection, which was done in some previous problem.

In practice, many code was used to tokenize the path into tokens, but it is not hard.

With TokenizePath, it's not hard to deep-pick one field, so we get PickFieldByPathTokens, just iterate tokens and recursively build an object with the only field.

There is an edge case, when path is '', PickField should return unknown, attention please.

The most hard part for me was this test case: Expect<Equal<DeepPick<Obj, 'a' | ''>, { a: number } & unknown>> I found before union2intersection, any type union with unknown will output unkown, so informantion got lost. So I have to wrap types when they are in union type and unwrap them when doing union2intersection. This is the reason for Wrap and Unwrap types, and modification for DeepPick and final version of Union2Intersection.

Solution by zhaoyao91 #21311

type Join<T extends Array<string>, U extends string | number> = T extends [
  infer H extends string,
  ...infer L extends string[]
]
  ? `${H}${L extends [] ? "" : U}${Join<L, U>}`
  : "";

type PopOther<T> = T extends [infer A, ...infer B] ? B : [];
type Pop<T> = T extends [infer A, ...infer B] ? A : [];
type SpiltString<
  T,
  SymbolDot extends string = ".",
  Result extends Array<string> = []
> = T extends `${infer A}${SymbolDot}${infer B}`
  ? SpiltString<B, SymbolDot, [...Result, A]>
  : [...Result, T];
type DeepPick_<T extends Record<string, any>, K extends string> = K extends ""
  ? never
  : {
      [P in Pop<SpiltString<K, ".">>]: P extends keyof T
        ? T[P] extends Record<string, any>
          ? Join<PopOther<SpiltString<K, ".">>, "."> extends ""
            ? T[P]
            : DeepPick<T[P], Join<PopOther<SpiltString<K, ".">>, ".">>
          : T[P]
        : never;
    };
type PickToUnIonFn<T> = T extends any ? (c: T) => void : never;

type DeepPick<T extends Record<string, any>, K extends string> = PickToUnIonFn<
  DeepPick_<T, K>
> extends (c: infer R) => void
  ? R
  : never;

Solution by so11y #21232

// your answers
type UnionToIntersection<U> = (
  U extends any ? (arg: U) => any : never
) extends (arg: infer I) => void
  ? I
  : never;

type GetProps<T,K> = K extends `${infer F}.${infer R}` ? 
F extends keyof T ? R extends '' ? Pick<T,F> : {
  [k in F] : GetProps<T[F & keyof T],R>
} : unknown : K extends keyof T ? Pick<T,K> : never

Solution by YqxLzx #21183

type UnionToIntersectionFn<U> = (U extends any ? (k: () => U) => void : never) extends (k: infer I) => void ? I : never;

type GetUnionLast<U> = UnionToIntersectionFn<U> extends () => infer R ? R : never;
type UnionToTuple<U, Last = GetUnionLast<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, Last>>, Last];

type ResolveKey<TObject, Key extends string> = Key extends `${infer TMain}.${infer TNested}` ? TMain extends keyof TObject ? GenerateObject<TObject[TMain], TNested> : never : Key extends keyof TObject ? TObject[Key] : never;
type GenerateObject<TObject, Key> = Key extends `${infer TMain}.${infer TNested}` ? TMain extends keyof TObject ? { [key in TMain]: GenerateObject<TObject[key], TNested> } : never : Key extends keyof TObject ? { [key in Key]: TObject[Key] } : never;

type GenerateIntersection<TObject extends object, Keys extends string> = UnionToTuple<Keys> extends [infer TFirst, ...infer TLast] ? NormalizeObject<RemoveNevers<{ [key in TFirst as key extends PropertyKey ? key extends `${infer TMain}.${infer TRest}` ? TMain : key : never]: key extends string ? ResolveKey<TObject, key> : never }>> & GenerateIntersection<TObject, Exclude<Keys, TFirst>> : {};


type RemoveNevers<TObject extends object> = Omit<TObject, keyof { [key in keyof TObject as TObject[key] extends never ? key : never]: never }>;
type NormalizeObject<TObject> = keyof TObject extends never ? unknown : TObject;

type DeepPick<TObject extends object, Keys extends string, Props extends GenerateIntersection<TObject, Keys> = GenerateIntersection<TObject, Keys>> = keyof Props extends never ? unknown : Props & DeepPick<Omit<TObject, keyof Props>, Keys>;

Solution by HiiiiD #20176

type UnionToIntersection<T> = (T extends any ? (a: T) => any : never) extends (a: infer R) => any ? R : never;

type PickOne<O, T> = T extends `${infer F}.${infer R}`
  ? (F extends keyof O ? { [Key in F]: DeepPick<O[F], R> } : unknown)
  : (T extends keyof O ? Pick<O, T> : never);

// If `T` is '', `PickOne<O, T>` would be `never`, so in union, it would be ignored.
// `Equal<{ a: string }, {a : string } & unknown> // true
type DeepPick<O, T extends string> = UnionToIntersection<
  T extends string ? PickOne<O, T> : never
>

Solution by logan70 #19678

type GetObj<T extends Record<string, any>, K> = K extends `${infer L}.${infer R}` ?
  L extends keyof T ?
    Record<L, GetObj<T[L], R>> :
    unknown :
  K extends keyof T ?
    Record<K, T[K]> :
    unknown;

type UnionFn<T extends Record<string, any>, U> = U extends unknown ? (args: GetObj<T, U>) => void : never;
type Intersection<T extends Record<string, any>, U> = UnionFn<T, U> extends (args: infer I) => void ? I : never;
type DeepPick<T extends Record<string, any>, U> = Intersection<T, U>;

Solution by CallMeSaltyF1sh #19616

type UnionToIntersectionFn<U> = (U extends any ? (k: () => U) => void : never) extends (k: infer I) => void ? I : never;

type GetUnionLast<U> = UnionToIntersectionFn<U> extends () => infer R ? R : never;

type UnionToTuple<U, Last = GetUnionLast<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, Last>>, Last];

type StringToArray<S, A extends string[] = []> = S extends `${infer F}.${infer R}` ? StringToArray<R, [...A, F]> : [...A, S];

type PickSingle<T, S extends string, A extends string[] = StringToArray<S>> = Equal<S, ''> extends true
  ? unknown
  : {
      [p in keyof T as p extends A[0] ? p : never]: A extends [infer F, ...infer R extends string[]] ? PickSingle<T[p], S, R> : T[p];
    };

type DeepPick<T, K, Total = unknown, IsFirst = true> = UnionToTuple<K> extends [infer F extends string, ...infer R] ? DeepPick<T, Exclude<K, F>, Total & PickSingle<T, F>> : Total;

Solution by CaoXueLiang #19079

// your answers

type Get<T, K extends string> = K extends `${infer Key}.${infer R}`
  ? Key extends keyof T
    ? { [P in keyof T as P extends Key ? P : never]: Get<T[Key], R> }
    : never
  : K extends keyof T
    ? { [P in keyof T as P extends K ? P : never ]: T[K] }
    : never

/**
 * UnionToFunc<1 | 2> => ((arg: 1) => void | (arg: 2) => void)
 */
 type UnionToFunc<T> = T extends unknown ? (arg: T) => void : never

 /**
  * UnionToIntersection<1 | 2> = 1 & 2
  */
 type UnionToIntersection<U> = UnionToFunc<U> extends (arg: infer Arg) => void
   ? Arg
   : never

type DeepPick<T, K extends string> = UnionToIntersection<Get<T, K>>

Solution by humandetail #16476

// your answers
type UnionToIntersection<
    U> =
    (U extends any ? (arg: U)=>any : never) extends (arg:infer R)=>any 
    ? R 
    : never ;

type DP<
    T extends object,
    U extends PropertyKey> =
    U extends ''
    ? never
    : U extends `${infer R extends string&keyof T}.${infer V}`
    ? T[R] extends object
    ? {[J in R]: DP<T[R],V>}
    : {}
    : {[J in U&keyof T]:T[J]} ;
    
type DeepPick<
    T extends object,
    U extends PropertyKey> =UnionToIntersection<DP<T,U>> ;

Solution by justBadProgrammer #15899

// your answers

type UnionToIntersection<U> = (U extends U
  ? (args: U) => void
  : never) extends (args: infer R) => void
    ? R
    : never;

type DeepPickHelp<O extends Record<string, any>, K> = K extends K
  ? K extends `${infer FirstKey}.${infer RestKey}`
    ? FirstKey extends keyof O
      ? {
        [Key in keyof O as Key extends FirstKey ? Key : never]: DeepPickHelp<O[Key], RestKey>
      }
      : never
    : K extends keyof O 
      ? {
        [Key in keyof O as Key extends K ? Key : never]: O[Key]
      }
      : never
  : never


type DeepPick<O extends Record<string, any>, K, SK = K> = [K] extends [''] 
  ? unknown
  : '' extends K
    ? UnionToIntersection<DeepPickHelp<O, Exclude<SK, ''>>> & unknown
    : UnionToIntersection<DeepPickHelp<O, K>>

Solution by GumplinGo #13019

// your answers
type UnionToIntersection<U> = (
  U extends any ? (arg: U) => any : never
) extends (arg: infer I) => void
  ? I
  : never;

type GetProps<T,K> = K extends `${infer F}.${infer L}` ? {[K in F]:GetProps<T[F & keyof T],L>} : K extends keyof T ? {[P in K ]:T[P]} : never 

type DeepPick<T,P> = UnionToIntersection<GetProps<T,P>>

Solution by flyFatSeal #12927

type Union2Intersection<TUnion> = (
  TUnion extends TUnion ? (param: TUnion) => void : never
) extends (param: infer Intersection) => void
  ? Intersection
  : never;

type SafeString<TValue> = TValue & string

type DeepPick<TObject, TPath> = Union2Intersection<
  TPath extends `${infer First extends SafeString<keyof TObject>}.${infer Rest}`
    ? { [key in First]: DeepPick<TObject[First], Rest> }
    : TPath extends keyof TObject
    ? { [key in TPath]: TObject[TPath] }
    : never
>;

Solution by michaltarasiuk #12636

// your answers
type UnionToIntersection<U, P = U> = (
  U extends P ? (a: U) => void : never
) extends (a: infer A) => void
  ? A
  : never;

type DeepPick<O, U> = UnionToIntersection<
  U extends `${infer A extends keyof O & string}.${infer Rest}`
  ? { [K in A]: DeepPick<O[A], Rest> }
  : U extends keyof O
  ? { [K in U]: O[U] }
  : never
>;

Solution by SeptEarlyMorning #12135

type DeepPick<Obj, Path extends string> = UnionToIntersection<
    Path extends `${infer First}.${infer Rest}` ? (
      UnionToIntersection<{ [K in First]: DeepPick<Obj[K & keyof Obj], Rest> }>
    ) : Path extends keyof Obj ? (
      { [P in Path & keyof Obj]: Obj[P] }
    ) : never>

type UnionToIntersection<T> =
  (T extends any ? (_: T) => any : never) extends
  (_: infer I) => any ? I : never

Playground

Solution by teamchong #11832

// your answers

type UnionToIntersection<U> = (U extends U ? 
  (x: U) => unknown :
  never) extends (x:infer P) => unknown ? 
  P :
  never;

type DeepPick<T extends Record<string, any>, P extends string> = UnionToIntersection<
  P extends P ?
    P extends `${infer Key}.${infer Rest}` ?
      {
        [K in Key]: DeepPick<T[K], Rest>
      } :
      P extends keyof T ?
        Pick<T, P> :
        never:
    never
>

Solution by Joyee691 #11721

type UnionToIntersect<T> = (T extends T ? (x: T) => unknown : never) extends (x: infer P) => unknown ? P : never;
type DeepPick<T extends Record<string, any>, K extends string> = UnionToIntersect<K extends K ?
  K extends `${infer Key}.${infer ChildrenKey}`
  ? {
    [K in Key]: DeepPick<T[Key], ChildrenKey>
  }
  : K extends keyof T
    ? Pick<T, K>
    : unknown
  : unknown
>;

Solution by XkSuperCool #10497