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

Typescript how to pick and combine nested type as root type

我只有一种类型,我想将这种类型重新用于连接数据:

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

我将从两个不同的角度来探讨这个问题。

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

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 等构建较小的类型)切换到自下而上的方法(定义然后将单个类型用作更复杂类型的构建块)。

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

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

根据您的示例,看起来所需的行为是选择 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;
}

我们可以使用 实用程序类型来解决这个问题。

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