TS类型展开

1,201 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第29天,点击查看活动详情

TS类型展开

在 TS 类型中,我们进行类型的调用,有时会不显示对应的结果,只显示相关类型的调用

例如下面几个例子:

1、keyof

type obj10 = {
  name: string
  age: number
}
// 这里我们想要显示的是:name | age
// 实际的显示:keyof obj10
type keyofTest = keyof obj10 

这里可以看出,TS 直接将对象的引用显示了出来,并没有将其内部展开

2、类型别名

type CxrObject = {
  age: number
}
type CxrUnion = boolean | number

2.1、union

// 想要显示:boolean | number | string
// 实际显示:string | CxrObject
type unionCase = CxrObject | string

2.2、嵌套

type obj11 = {
  c: CxrObject // 这里希望展开
  // c: CxrUnion // 注意,之后的实现方法有个缺陷:对象中的 union 无法展开(但是单纯的union可以展开)
  name: string
}

实现

keyof

对其进行判断后返回,即可展开

type Expand<T> = T extends infer O ? O : never
type r1 = Expand<keyofTest> // "name" | "age"

此时 r1 显示的内容:type r1 = "name" | "age"

类型别名

type r2 = Expand<unionCase> // string | CxrObject

展开类型别名(发现此时 Expand 并没有起作用),因为这里我们遇到的是对象,对象需要额外处理一下

  • 改造 Expand 函数
type Expand<T> = T extends infer O 
	? {[K in keyof O]: O[K]} 
	: never
type r3 = Expand<unionCase> // type r3 = string | { age: number; }

此时可以看到类型别名也已经展开了

递归/嵌套

type r4 = Expand<obj11> // type r4 = { c: CxrObject; name: string; }

如果传入的参数中有对象嵌套,则我们的 Expand 函数就又不好使了,那么我们接下来对其进行递归一下

  • 改造 Expand 函数
type Expand<T> = T extends object 
  ? T extends infer O 
    ? {[K in keyof O]: Expand<O[K]>}
    : never
  : T
type r5 = ExpandRecursive<obj11> // { c: { age: number; }; name: string; }

此时可以看到,我们已经正常展开了~

注意

这个方法有一个缺陷:如果对象内嵌套了联合类型,那么就没办法正常展开

type CxrUnion = boolean | number
type obj11 = {
  c: CxrUnion // 对象中的 union 无法展开(但是单纯的union可以展开)
  name: string
}

最终代码

type Expand<T> = T extends object 
  ? T extends infer O 
    ? {[K in keyof O]: Expand<O[K]>}
    : never
  : T