ObjectEntries
大约 2 分钟
ObjectEntries
题目
实现类型版本的 Object.entries。
interface Model {
name: string;
age: number;
locations: string[] | null;
}
type modelEntries = ObjectEntries<Model>;
// ['name', string] | ['age', number] | ['locations', string[] | null]解答
思路
核心思路是 Object.entries 返回键值对数组。在 TypeScript 类型系统中,俺们需要:
- 遍历对象的每个键
- 为每个属性创建元组
[Key, Value] - 返回所有元组的联合类型
难点在于处理可选属性。当属性是可选的 (key?: Type) 时,其值类型变为 Type | undefined。但 Object.entries 实际上不会包含这个 undefined —— 它只是可能不包含这个条目。
基础解法
type ObjectEntries<T> = {
[K in keyof T]-?: [K, T[K]]
}[keyof T]等等 —— 这还不太对。-? 移除了可选修饰符,但 T[K] 对于可选属性仍然包含 undefined。
处理可选属性
俺们需要从来自可选属性的值类型中剥离 undefined:
type ObjectEntries<T> = {
[K in keyof T]-?: [K, T[K] extends undefined ? undefined : Exclude<T[K], undefined>]
}[keyof T]但这有个问题 —— 如果 undefined 是类型的显式部分(而非来自可选性)呢?
最终解法
type ObjectEntries<T, U = Required<T>> = {
[K in keyof U]: [K, U[K]]
}[keyof U]通过使用 Required<T>,俺们:
- 移除所有可选修饰符
- 这也移除了因可选性添加的
undefined - 保留联合类型中显式的
undefined(如string | undefined)
不过这种方法会把显式声明的 undefined 也去掉。更精确的解法:
type RemoveUndefined<T> = [T] extends [undefined] ? T : Exclude<T, undefined>
type ObjectEntries<T> = {
[K in keyof T]-?: [K, RemoveUndefined<T[K]>]
}[keyof T]这样只有当类型仅为 undefined 时才保留它。
深入分析
为什么用 -? 修饰符?
-? 修饰符移除属性的可选标记。没有它,可选属性会创建类似 ['key', Type | undefined] 的条目。
映射类型转联合
{ [K in keyof T]: ... }[keyof T] 是一个常见模式:
- 首先创建一个转换后值的映射类型
- 然后用
keyof T索引它,得到所有值的联合类型
边界情况
考虑这些场景:
- 可选属性:
{ name?: string }→['name', string] - 显式 undefined:
{ name: string | undefined }→['name', string | undefined](应保留undefined) - 仅 undefined:
{ name: undefined }→['name', undefined]
要点总结
-?移除可选性 —— 正确类型化条目的关键- 映射类型转联合模式 ——
{ [K in keyof T]: F<K> }[keyof T]转换为联合 - 可选 vs 显式 undefined —— 它们行为不同,需要仔细处理
Required<T>同时移除?和它添加的undefined
