首页 > 解决方案 > 在 TypeScript 中选择并展平类型签名

问题描述

我正在查看标准库中包含的一些更酷的类型函数,然后开始玩。在 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'])

标签: typescript

解决方案


我们可以通过创建所选属性类型的交集来实现这一点。

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由@jcalz在这里提供,你应该在那里看到解释(不要忘记用upvote 来感谢他:))。T[K]会给我们密钥的类型K。IfK是键的联合,T[K]将是联合中所有属性类型的联合。

要在函数中使用它,我们需要两个类型参数,一个代表我们选择的类型,一个代表我们选择的键。

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; }

推荐阅读