TypeScript 中常用的工具类型

71 阅读3分钟

keyof 和 in

对于任何类型 T, keyof T 的结果为该类型上所用共有属性key的联合类型:

interface Point {
  x: number
  y: number
}
// P = 'x' | 'y'
type P = keyof Point

现在我们编写来一个函数获取任意对象的属性值,一开始我们可能会这样写:

const foo = {
  hello: 'world'
}
function getValue(obj: Object, key: string) {
  return obj[key]
}

但是这样写会报错:

image-20220713202025414

这时我们可以使用 keyof 来加强该函数的功能:

function getValue<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

in 可以迭代联合类型中的所有项:

type Foo = 'x' | 'y'
type Bar = { [P in Foo]: number }
// 相当于
// type Bar = {
//   x: number;
//   y: number;
// }

条件类型

类似于js里的三目运算符:

// type A = true
type A = 'x' extends 'x' ? true : false
// type B = 2
type B = 'x' | 'y' extends 'x' ? 1 : 2
// type D = 1 | 2
type C<T> = T extends 'x' ? 1 : 2
type D = C<'x' | 'y'>
// type F = 2
type E<T> = [T] extends 'x' ? 1 : 2
type F = E<'x' | 'y'>

这里需要注意的是:

  • 如果用于简单的条件判断,则是直接判断前面的类型是不是 extends 后面的类型。

  • 如果前面的类型是泛型,并且是联合类型,则联合类型会被分解为一个个子类型,分别判断是否 extends 后面的类型。

  • 想要阻止分类,使用元组类型包裹即可 [T]

infer

使用 infer 可以在 extends 条件类型中动态推断当前的类型,例子如下:

type Foo<T> = T extends { a: (x: infer K) => infer U } ? K | U : never
// type Bar = string | number
type Bar = Foo<{ a: (x: number) => string }>

Partial、Required 和 Readonly

Partial<T>T的所有属性变成可选的:

type Partial<T> = {
  [P in keyof T]?: T[P]
}
  • [P in keyof T] 遍历T上的所有属性
  • ?: 让属性变为可选的
  • T[P] 取得原来的属性值

Required<T>T的所有属性变成必选的:

type Required<T> = {
  [P in keyof T]-?: T[P]
}

Readonly<T>T的所有属性变成只读的:

type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}

Pick 和 Record

Pick<T, K>T 中挑选一组属性 K 并组成一个新的类型:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}
// type Foo = { a: number }
type Foo = Pick<{a: number, b: number}, 'a'>

Record<K, T> 构造一个新的类型,key 为联合类型 T 中的每个子类型,类型为 K

// keyof any 的结果是 string | number | symbol,因为 key 只能是这三种类型
type Record<K extends keyof any, T> {
  [P in K]: T
}
// type Foo = { a: stirng; b: string }
type Foo = Record<'a' | 'b', string>
// type Bar = { [key: string]: string }
type Bar = Record<string, string>

Exclude、Extract 和 Omit

Exclude<T, U> 提取存在于T,但不存在于U的类型组成新的类型:

type Exclude<T, U> = T extends U ? never : T
// type Foo = 'b'
type Foo = Exclude<'a' | 'b', 'a' | 'c'>

Extract<T, U> 取交集:

type Extract<T, U> = T extends U ? T : never
// type Foo = 'a'
type Foo = Exclude<'a' | 'b', 'a' | 'c'>

Omit<T, K> 从类型 T 中忽略 K 中的所有属性:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
interface User {
  age: number
  name: string
}
// type Foo = interface User { name: string }
type Foo = Omit<User, 'age'>

Parameters 和 ReturnType

Parameters 获取函数的参数类型,并把他们放到一个元组中。

type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never
// type Foo = [a: number, b: number]
type Foo = Parameters<(a: number, b: number) => void>

Parameters 获取函数的返回值。

type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any
// type Foo = string
type Foo = Parameters<(a: number, b: number) => string>