TypeScript 高级内置类型详解:Record, Partial, Readonly, Pick, Exclude, Omit

作者:青柠

时间:2026-10-25

浏览:503

本文深入解析 TypeScript 中的高级内置类型,包括 Record、Partial、Readonly、Pick、Exclude 和 Omit。通过源码剖析与实战案例,详细讲解 keyof、映射类型及条件类型的实现原理,帮助开发者掌握类型转换与约束技巧,提升代码的类型安全性与可维护性。

文章目录

TypeScript 是一种静态类型检查语言,允许开发者为变量、函数参数、返回值以及对象属性明确指定类型,从而在编译阶段捕获潜在错误。

本文将系统介绍 TypeScript 中常用的高级内置类型,包括 Record、Partial 和 Readonly 等,并通过源码解析其内部实现机制。

联合类型

/* 首先是联合类型的介绍 */
let a: string | number = '123' // 变量a的类型既可以是string,也可以是number
a = 123

keyof

keyof 操作符用于提取一个类型的所有属性名,并将这些属性名组合成一个新的联合类型。

// 1. 定义一个接口
interface Person {
  name: string
  age: number
}

type PersonKeys = keyof Person // 等同于 type PersonKeys = 'name' | 'age'

const p1: PersonKeys = 'name' // 可以
const p2: PersonKeys = 'age' // 可以
const p3: PersonKeys = 'height' // 不能将类型“"height"”分配给“name | age”

Record

Record 类型主要用于构建属性映射关系。为了便于理解,下面直接通过具体案例进行说明。

首先定义一个基础的对象类型。

联合类型

Record 可以配合联合类型使用,实现更复杂的映射逻辑。

联合类型

此外,Record 还可以用于定义对象结构,使得对象的每个属性都对应一个具有特定键值对结构的类型。

联合类型

理解 Record 的实现原理有助于深入掌握其用法。Record 的内部定义接收两个泛型参数。

type Record<K extends string | number | symbol, T> = {
    [P in K]: T;
}

下面对 Record 的定义进行逐步解析。

  • 第一个泛型参数 K 代表键的类型。
  • 语法中的 `in` 关键字表示遍历操作。例如,将类型 string 进行遍历,即遍历 string 中的所有字符。
  • 映射类型中的每个属性值都对应传入的第二个泛型参数 T,例如将键 string 映射为类型 PersonModel。

Partial (部分的; 不完全的)

在 TypeScript 中,Partial 工具类型的作用是将一个接口或类型定义中的所有属性转换为可选属性。

// 定义一个Person接口
interface Person {
  name: string
  age: number
}

// 但是我们有可能数据是请求过来的,刚开始我们需要定义一个空对象,如下。
const person1: Person = {}
/**
  但是这样就加粗样式会出现报错,类型“{}”缺少类型“Person”中的以下属性: name, age。
  可能我们可以更改定义方式如下,但是有的时候我们不想破坏事先的定义,或者不能破坏
  interface Person {
    name?: string
    age?: number
  }
*/

/**
  那这个时候我们就可以用到typescript自带的高级类型 Partial,就相当于将上方接口所有属性变成可选的
  将我们需要定义的类型当做泛型传入Partial中,那么就相当于当前的类型里面的所有属性都是可选的
 */
const person2: Partial<Person> = {} // 可以
const person3: Partial<Person> = { name: 'xiaodu' } // 可以
const person4: Partial<Person> = { height: 1.88 } // 报错 “height”不在类型“Partial<Person>”中

接下来分析 Partial 的实现原理,查看其内部定义。

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

对 Partial 的定义进行逐步解析。

  • 首先利用 keyof 提取类型的所有属性名,形成联合类型。
  • 接着对提取出的属性名(如 age 和 name)进行遍历。
  • 在映射语法 `[P in keyof T]?` 中,问号 `?` 表示该属性为可选参数。
  • `T[P]` 表示获取原类型中对应属性的值类型,例如 `Person['name']` 返回 string 类型。

Required(必须的)

Required 类型与 Partial 相反,它的作用是将一个定义中的所有属性强制转换为必选属性。

联合类型

分析 Required 的实现原理,查看其内部定义。

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

对 Required 的定义进行逐步解析。

  • 同样首先利用 keyof 提取类型的所有属性名,形成联合类型。
  • 接着对提取出的属性名(如 age 和 name)进行遍历。
  • 在映射语法中,属性名前添加 `-` 符号(如 `-?`)表示移除可选标记,使该属性变为必选。
  • `T[P]` 表示获取原类型中对应属性的值类型,例如 `Person['name']` 返回 string 类型。

Pick(选择)

在 TypeScript 中,Pick 工具类型允许从原有的接口或类型中选择一部分属性定义,构建新的类型。

联合类型

如果需要选择多个特定的属性定义,可以通过联合类型指定键名。

联合类型

分析 Pick 的实现原理。

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

Pick 类型接收两个泛型参数。

  • 第一个泛型 T 代表原始的 interface 或 type 定义。
  • 第二个泛型 K 代表从 T 中选择的属性键,`extends` 关键字用于约束 K 必须是 T 的属性子集。

`K extends keyof T` 等同于 `K extends 'name' | 'age'`,意味着 K 只能是 name 或 age 中的一个或组合。

Readonly (意思是只读的)

在 TypeScript 中,Readonly 工具类型的作用是将一个定义中的所有属性转换为只读属性。

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

联合类型

可以看到 Readonly 可以将接口所有属性变为只读的不可修改的,但是是浅层的。接下来分析 Readonly 的实现原理,查看其内部定义。

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

对 Readonly 的定义进行逐步解析。

  • 首先利用 keyof 提取类型的所有属性名,形成联合类型。
  • 接着对提取出的属性名(如 age 和 name)进行遍历。
  • `readonly` 修饰符用于标记属性为只读,禁止后续赋值。
  • `T[P]` 表示获取原类型中对应属性的值类型,例如 `Person['name']` 返回 string 类型。

Exclude(排除)

TypeScript 中的 Exclude 工具类型用于从联合类型中排除一部分内容。需要注意的是,Exclude 主要操作联合类型。

联合类型

分析 Exclude 的实现原理。

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

Exclude 类型传入两个泛型参数。

  • 这里使用 MyTypes 即 `'name' | 'age' | 'height'` 来代表泛型 T。
  • 使用 name 属性来代表第二个泛型 U。
  • `T extends U` 用于判断 T 是否包含 U 的类型。如果 T 包含 U(如 'name' | 'age' | 'height' 包含 'name'),则返回 never,从而实现将其排除的效果。

Omit (省略的)

在 TypeScript 中,Omit 工具类型用于从接口或类型中删除一部分键值对,生成新的类型。

联合类型

联合类型

Omit 的实现原理正是基于上述讲解的知识点(Exclude 和 Record)拼凑而成的。

type Omit<T, K extends string | number | symbol> = {
  [P in Exclude<keyof T, K>]: T[P]
}

ReadonlyArray(只读数组)

ReadonlyArray 用于创建一个数组,其索引不允许被修改。

我们知道当我们使用 const 创建对象或者数组时,其实是可以修改其内部属性的,但是有的时候我们可能不需要其内部能够被修改。

const arr: number[] = [1, 2, 3, 4, 5]
arr[0] = 3 

方法1:通过类型断言的方式实现只读约束。

联合类型

联合类型

方法2:使用 ReadonlyArray,需要传入一个泛型来约束数组中的元素类型。

联合类型

联合类型

这个时候你是不是就想知道 as const 和 ReadonlyArray 这两者的区别在哪里?

区别在于 as const 是深层次的,尽管数组内放的对象,对象内部数据也是不能被修改的。ReadonlyArray 则是‘浅层’的。

as const 断言示例。

联合类型

联合类型

ReadonlyArray 使用示例。

联合类型

可以看到修改 ReadonlyArray 数组内的第二层其实是可以的,这验证了其浅层只读的特性。

到此这篇关于 Typescript 高级类型 Record, Partial, Readonly 等介绍的文章就介绍到这了,更多相关 Typescript 高级类型 Record 内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章