type UninoToCross<T> = (T extends T ? (arg: T) => unknown : never) extends (arg: infer P) => unknown ? P : never
type DeepPick<T extends { [key: PropertyKey]: any }, P extends string> = UninoToCross<P extends keyof T ? Pick<T, P> : (P extends `${infer R}.${infer rest}` ? {
[K in R]: DeepPick<T[R], rest>
} : never)>
Solution by ouzexi #34275
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 gearonixx #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>
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
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