Skip to content

ObjectEntries

约 655 字大约 2 分钟

2022-12-01

题目

Github: ObjectEntries

实现 Object.entries 的类型版本

interface Model {
  name: string
  age: number
  locations: string[] | null
}
type modelEntries = ObjectEntries<Model>
// ['name', string] | ['age', number] | ['locations', string[] | null];

解题思路

在 Typescript 中,条件类型推断是分布式的,因此我们可以通过条件类型推断,从 对象类型 中,利用 key 推断 形成新的元组:

type ObjectEntries<T extends object, K extends keyof T = keyof T>
 = K extends K ? [K, T[K]] : never

也许会觉得到这里这个挑战就完成了,但是实际情况稍微有些麻烦。

我们知道,在 对象类型中,存在 必要属性 和 可选属性:

interface Mode {
  
name
: string // 必要属性
arg
?: number // 可选属性
}

那么 Mode 应用 ObjectEntries, 得到的结果为:

type 
a
=
ObjectEntries
<Mode>
//

可以看到,在结果中, argvalue 类型被转为了包含 undefined 的 联合类型,它应该只是 number 类型 。

这里我们可以通过 Required<T> 将 对象类型的所有属性全部转为 必要属性,以消除 可选属性中的 undefined

type ObjectEntries<T extends object, K extends keyof T = keyof T>
 = K extends K ? [K, Required<T>[K]] : never
type 
a
=
ObjectEntries
<Mode>
//

但是,如果可选属性的值类型就是 undefined 呢?

interface Mode {
  
name
: string // 必要属性
arg
?: undefined // 可选属性
} type
a
=
ObjectEntries
<Mode>
//

可以看到,其结果为 ['arg', never],很明显不符合预期,它应该是 undefined 类型。 针对此情况,还需要通过条件类型推断对 值类型为 undefined 进行处理。

答案

type ObjectEntries<T extends object, K extends keyof T = keyof T>
 = K extends K ? [K, T[K] extends undefined ? undefined : Required<T>[K]] : never

验证

interface Model {
  
name
: string
age
: number
locations
: string[] | null
} type
ModelEntries
= ['name', string] | ['age', number] | ['locations', string[] | null]
type
cases
= [
Expect
<
Equal
<
ObjectEntries
<Model>,
ModelEntries
>>,
Expect
<
Equal
<
ObjectEntries
<
Partial
<Model>>,
ModelEntries
>>,
Expect
<
Equal
<
ObjectEntries
<{
key
?: undefined }>, ['key', undefined]>>,
Expect
<
Equal
<
ObjectEntries
<{
key
: undefined }>, ['key', undefined]>>,
Expect
<
Equal
<
ObjectEntries
<{
key
: string | undefined }>, ['key', string | undefined]>>,
]

参考