To achieve this, we can create an intersection by combining the selected property types.
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type PickAndFlatten<T, K extends keyof T> = UnionToIntersection<T[K]>;
The UnionToIntersection
function was shared by @jcalz in this thread. You can find a detailed explanation there and make sure to show appreciation with an upvote :) Using T[K]
helps us retrieve the type of the specified key K
. If K
consists of multiple keys in a union, T[K]
will be a union of all the corresponding property types.
In order to implement this in a function, we require two type parameters – one for the type we are selecting from and another for the keys being picked.
interface OriginalType {
A: { a: string };
B: { b1: string, b2: number };
C: {};
D: { c: string };
}
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type PickAndFlatten<T, K extends keyof T> = UnionToIntersection<T[K]>;
function pickAndFlatten<T, K extends keyof T>(o: T, keys: K[]): PickAndFlatten<T, K> {
// Implementation not tested
const entries = Object.entries(o)
.filter(([k, v]) => keys.includes(k as K))
.map(([k, v]) => v);
return Object.assign({}, entries) as any;
}
let o: OriginalType;
const r = pickAndFlatten(o, ['A', 'B']); // { a: string; } & { b1: string; b2: number; }