如何在 TypeScript 中获取对象中所有键值对的联合类型?

Jor*_*Jor 6 typescript

假设我有一个这样的对象:

const person = {
  id: 87
  name: 'Some Name',
  address: {
    street: 'Some street',
    country: 'Some country'
  }
}
Run Code Online (Sandbox Code Playgroud)

我想获得一个所有键值对的并集类型。所以类型应该是:

{ id: number } | { name: string } | { address: { street: string; country: string; } }
Run Code Online (Sandbox Code Playgroud)

那怎么办呢?我试过这个:

type PersonInfo = {
  id: number;
  name: string;
  address: {
    street: string;
    country: string;
  }
}

type PersonMap<M extends { [index: string]: any }> = {
  [Key in keyof M]: M[Key]
};

type PersonTest1 = PersonMap<PersonInfo>[keyof PersonMap<PersonInfo>];
// Returns "number | string | { street: string; country: string; }"

type PersonTest2 = PersonMap<PersonInfo>;
// Returns { id: number; name: string; address: { street: string; country: string;} }
Run Code Online (Sandbox Code Playgroud)

如何获得上述联合类型?

jca*_*alz 5

似乎您想要一个以下形式的类型函数UnionOfSingleKeyObjects<T>,它将对象类型转换T为单键对象类型的联合,其中每个键keyof T只出现一次。根据您的用例,您可以定义这样的类型函数:

type UnionOfSingleKeyObjects<T extends object> = 
  { [K in keyof T]-?: { [P in K]: T[P] } }[keyof T]
Run Code Online (Sandbox Code Playgroud)

并验证它是否按PersonInfo预期工作:

type PersonKVPairs = UnionOfSingleKeyObjects<PersonInfo>
/* type PersonKVPairs = {
    id: number;
} | {
    name: string;
} | {
    address: {
        street: string;
        country: string;
    };
} */
Run Code Online (Sandbox Code Playgroud)

的定义UnionOfSingleKeyObjects是一个映射类型K,其中我们迭代 中的每个键类型keyof T,计算每个键的相关单键对象,然后对其进行索引keyof T获得所有单键对象类型的并集。

您可以使用分布式条件类型来获得相同的效果,而不是索引到映射类型:

type UnionOfSingleKeyObjects<T extends object> =
    keyof T extends infer K ? K extends keyof T ?
    { [P in K]: T[P] } : never : never
Run Code Online (Sandbox Code Playgroud)

无论哪种方式都有效;我倾向于使用映射类型,因为它们比条件类型分布性更容易解释。


在这两种方法中,带有 key 的单键对象都K写为{[P in K]: T[P] }。这也可以写为Record<K, T[K]>使用实用程序Record<K, V>类型,或Pick<T, K>使用实用程序Pick<T, K>类型。这些其他版本有其优点和缺点,并且可能会改变类型在 IntelliSense 快速信息中的显示方式以及输出中可选/readonly键是否保持可选/ 。readonly如果您关心保留这些修饰符并且不想在您的快速信息中看到Pick或,您可以像这样Record编写:{[P in keyof Pick<T, K>]: T[P]}

type UnionOfSingleKeyObjects<T extends object> =
    { [K in keyof T]-?: { [P in keyof Pick<T, K>]: T[P] } }[keyof T]
Run Code Online (Sandbox Code Playgroud)

我们可以看到这样的修饰符被保留了:

type Example = UnionOfSingleKeyObjects<{ a?: string, readonly b: number }>
/* type Example = {
    a?: string;
} | {
    readonly b: number;
} */
Run Code Online (Sandbox Code Playgroud)

同样,根据您的用例,您可能会也可能不关心这些事情。

Playground 代码链接