打字稿如何选择和组合嵌套类型作为根类型

时间:2021-03-06 08:01:52

标签: typescript typescript-typings

我有单一类型,我想将此类型重用于连接数据:

interface MetaData {
  user: {
    id: string;
    fullName: string;
  },
  stats: {
    following: number;
  },
  another: {
    any: any;
  }
}

我想通过键组合一些嵌套类型

(例如:type Profile = PickAllNestedKey<MetaData, 'user' | 'stats'>

我必须尝试这个代码,但它只能选择一个键

type NestedPartial<T, K extends keyof T> = {
   [P in keyof T[K]]: T[K][P]; 
};
type Profile = NestedPartial<Metadata, 'user'>;

预期类型如:

interface Profile {
  id: string;
  fullName: string;
  following: number;
}

2 个答案:

答案 0 :(得分:2)

我会从两个不同的角度来解决这个问题。

第一个是质疑类型的结构。为什么没有明确定义嵌套类型?也许可以改进类型结构?这是我的意思:

interface User {
  id: string;
  fullName: string;
}

interface Stats {
  following: number;
}

interface Another {
  any: any;
}

interface MetaData {
  user: User,
  stats: Stats,
  another: Another
}

type Profile = User & Stats

定义一次针对一个实体的更小和原子类型大大提高了可读性和代码维护。然后,您可以在任何您认为合适的地方自由使用较小的类型,而无需从大型类型创建它们。换句话说,这将方法从自上而下的方法(创建大类型,然后使用 PickOmit 等构建较小的类型)转换为自下而上的方法(定义然后将各个类型用作更复杂类型的构建块)。

也就是说,我们并不总是能够控制我们正在使用的类型。因此,您可以在以下几个地方找到有关如何使用嵌套 Pick 的指导:

  1. https://gist.github.com/staltz/368866ea6b8a167fbdac58cddf79c1bf
  2. https://github.com/piotrwitek/utility-types

答案 1 :(得分:1)

根据您的示例,看起来所需的行为是选择 MetaData 的顶级属性,然后“展平”结果。

您当前尝试的问题是 keyof T[K]。如果 K 是像 'user' | 'stats' 这样的联合,那么 T[K] 是两个对象 { id: string; fullName: string; } | { following: number; } 的联合。 该联合中没有共享密钥。因此,没有 P 可分配给 P in keyof T[K]

如果我们只是 Pick 我们想要的属性并通过键索引它们,那么我们得到几乎所需的类型,除了它是一个联合并且我们想要一个交集。 Pick<T, K>[K] 解析为:

{
    id: string;
    fullName: string;
} | {
    following: number;
}

我们可以使用 @jcalz's UnionToIntersection<T> 实用程序类型来解决这个问题。

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

type NestedPartial<T, K extends keyof T> = UnionToIntersection<Pick<T, K>[K]>;

type Profile = NestedPartial<MetaData, 'user' | 'stats'>;

决定:

type Profile = {
    id: string;
    fullName: string;
} & {
    following: number;
}

Typescript Playground Link