Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pick and flatten a type signature in TypeScript

Tags:

typescript

I was looking over some of the cooler type functions that are included in the standard library and I got around to playing. Is something like this actually possible in TypeScript?

interface OriginalType {
    A: { a : string }
    B: { b1 : string, b2: number }
    C: {}
    D: {c: string}
}


const r : {a: string, b1: string, b2: string} = pickAndFlatten<OriginalType>(['A', 'B'])
like image 413
Anthony Naddeo Avatar asked Jul 20 '18 05:07

Anthony Naddeo


1 Answers

We can so this by creating an intersection of the picked 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]>;

UnionToIntersection was provided by @jcalz here and you should see the explanation there (don't forget to thank him with an upvote :) ). T[K] will give us the type of the key K. If K is a union of keys, T[K] will be a union of all property types in the union.

To use this in a function we will need two type parameters, one representing the type we are picking from an one to represent the keys we are picking.

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> {
  // Untested implementation
  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; }
like image 96
Titian Cernicova-Dragomir Avatar answered Sep 23 '22 06:09

Titian Cernicova-Dragomir