TypeScript 工具类型

Flying
2021-11-26 / 0 评论 / 118 阅读 / 正在检测是否收录...

当你不想重复工作时,有时需要基于已有的类型创建另一种新类型。TypeScript 提供了几种实用工具类型,以方便开发人员进行常见的类型转换。这些工具类型在全局范围内可用,从源代码能看出他们很多是基于索引签名和映射类型的语法。

tool-ts.svg

映射类型基于索引签名的语法,索引签名用于声明事先未声明的属性类型。

要看懂工具类型源代码,得了解一下索引签名。

索引签名

在 TypeScript 中,索引签名是一种用于定义对象的属性类型的方式。它允许我们按照特定的索引类型来访问对象的属性,并确定相应的属性值类型。

使用索引签名,我们可以定义对象的索引类型和对应的属性值类型。它有两种形式:字符串索引签名和数字索引签名。

字符串索引签名

字符串索引签名允许我们使用字符串来访问对象的属性,并确定该属性的值的类型。例如:

interface Person {
  [key: string]: string;
}

const person: Person = {
  name: "John",
  age: "30",
};

const name: string = person["name"]; // 可以访问 name 属性并确定其为字符串类型
const age: string = person["age"]; // 可以访问 age 属性并确定其为字符串类型
const address: string = person["address"]; // 无法确定 address 属性的类型

数字索引签名

数字索引签名允许我们使用数字来访问对象的属性,并确定该属性的值的类型。例如:

interface Data {
  [index: number]: string;
}

const data: Data = ["apple", "banana", "cherry"];

const fruit: string = data[0]; // 可以访问索引为 0 的元素,并确定其为字符串类型
const invalidValue: string = data[10]; // 无法确定索引为 10 的元素的类型

通过使用索引签名,我们可以灵活地定义对象的属性类型,使其适应不同的场景和需求。

Partial & Required & Readonly

  • 源码
type Partial<T> = {
  [P in keyof T]?: T[P]
}

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

type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}
  • 作用
  1. Partial:让 T 中的所有属性都是可选的
  2. Required:让 T 中的所有属性都是必需的
  3. Readonly:让 T 中的所有属性都是只读的
  • 示例
interface User {
  id: number
  age?: number
  name: string
}

// 相当于: type Optional = { id?: number | undefined; age?: number | undefined; name?: string | undefined; }
type PartialUser = Partial<User>
// 相当于: type RequiredUser = { id: number; age: number; name: string; }
type RequiredUser = Required<User>
// 相当于: type ReadonlyUser = { readonly id: number; readonly age: number | undefined; readonly name: string; }
type ReadonlyUser = Readonly<User>
PartialReadonly 类型分别对应映射时的两个附加修饰符: readonly?。Required类型在可选修饰符 ?加了前缀 -,表示移除可选性,换句话说就是必选的。

Pick & Omit

  • 源码
type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
  • 作用
  1. Pick:从 T 中选择一组键属于联合类型 K 的属性来构造类型。(加法)
  2. Omit:使用除联合类型 K 以外的 T 的属性来构造类型。(减法)
  • 示例
interface User {
  id: number
  age?: number
  name: string
}

// 相当于: type PickedUser = { id: number; age?: number | undefined; }
type PickedUser = Pick<User, 'id' | 'age'>

// 相当于: type OmittedUser = { age?: number | undefined; name: string; }
type OmittedUser = Omit<User, 'id'>

如果需要数据层级很深,我们可以通过结合条件类型,递归 Partial 类型来实现。

type DeepPartial<T> = T extends Function
  ? T
  : T extends object
  ? { [P in keyof T]?: DeepPartial<T[P]> }
  : T

type PartialObject = DeepPartial<object>

Exclude & Extract & NonNullable

  • 源码
type Exclude<T, U> = T extends U ? never : T

type NonNullable<T> = T extends null | undefined ? never : T

type Extract<T, U> = T extends U ? T : never
  • 作用
  1. Exclude:从 T 中排除那些可以分配给 U 的类型来构造类型(差集)
  2. NonNullable: 从 T 中排除 nullundefined来构造类型
  3. Extract: 从 T 中提取可分配给 U 的类型来构造类型(交集)
  • 示例
type Coordinate = 'x' | 'y' | 'z' | undefined
// 相当于: type Excluded = 'z' | undefined
type Excluded = Exclude<Coordinate, 'x' | 'y'>
// 相当于: type NonNullabled = x' | 'y' | 'z'
type NonNullabled = NonNullable<Coordinate>
// 相当于: type Extracted = 'x' |' y'
type Extracted = Extract<Coordinate, 'x' | 'y'>
从语义上讲, Pick 相当于 Extract,Omit 相当于 Exclude。NonNullable 相当于 Exclude<Coordinate, undefined | null>。Pick 和 Omit 适用于对象、类和接口类型,Extract 和 Exclude 一般适用于联合和字面量类型。

Record

  • 源码
type Record<K extends keyof any, T> = {
  [P in K]: T
}
  • 作用

K 中的每个属性作为 key 值,以 T 作为 value 构造一个 map 类型。

  • 示例
type Row = 'dog' | 'cat'
interface Field {
  name: string
  age: number
}

// 相当于: type Pet = { dog: Field; cat: Field; }
type Pet = Record<Row, Field>

const pets: Pet = {
  dog: {
    name: 'wangcai',
    age: 1
  },
  cat: {
    name: 'bosimao',
    age: 2
  }
}

可以看到 Pet 类型是由 Record<Row, Field> 构造的。将 Row 中的每个值 ('dog' | 'cat') 与 Field 分组合并。Row 类型值作为 Pet 类型的键,Field 类型的值作为 Pet 类型的每个键对应的值。

Record 构造类型有点像数据库中的表结构,Row 表示行,Field 表示列。

参考

0

评论 (0)

取消