文章目录

TypeScript 工具类型Partial与Pick的实现原理

发布于 2026-04-14 01:25:06 · 浏览 12 次 · 评论 0 条

TypeScript 工具类型Partial与Pick的实现原理

TypeScript 提供了许多内置的工具类型,其中 PartialPick 是在日常开发中最高频使用的两个。它们本质上是对映射类型的封装,通过特定的语法糖来动态生成新的类型。要掌握它们,首先需要理解映射类型的运行机制。


前置知识:映射类型

在深入源码之前,理解 基础的映射语法至关重要。映射类型类似于 JavaScript 中的 for...in 循环,但它是在类型层面进行遍历。

创建 一个简单的映射类型:

type MyPick<T, K> = {
    [P in K]: T[P]
}

这段代码的逻辑如下:

  1. [P in K]:遍历联合类型 K 中的每一个类型,将其赋值给变量 P(Property)。
  2. T[P]:使用索引访问类型,获取原对象类型 T 中键名为 P 的值的类型。

这是构建所有高级工具类型的基石。


Partial 的实现原理

Partial<T> 的作用是将类型 T 中的所有属性变为可选的。

1. 核心源码分析

查看 TypeScript 的核心实现代码:

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

2. 逻辑拆解

假设我们有一个用户接口:

interface User {
    id: number;
    name: string;
    age: number;
}

Partial<User> 被执行时,发生以下过程:

  1. 获取 键集合:keyof T 获取到 "id" | "name" | "age"
  2. 遍历 属性:[P in keyof T] 相当于依次让 P 等于 "id""name""age"
  3. 添加 可选修饰符:? 符号被添加到每个属性键的后面。
  4. 推导 值类型:T[P] 保持原类型不变(number 还是 numberstring 还是 string)。

最终生成的类型等价于:

type PartialUser = {
    id?: number;
    name?: string;
    age?: number;
}

3. 实际应用场景

当你需要更新一个对象,但只想修改其中部分字段时,使用 Partial

function updateUser(user: User, fieldsToUpdate: Partial<User>) {
    return { ...user, ...fieldsToUpdate };
}

Pick 的实现原理

Pick<T, K> 的作用是从类型 T 中选取一组属性 K,构造一个新的类型。

1. 核心源码分析

查看 TypeScript 的核心实现代码:

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

2. 逻辑拆解

这里引入了泛型约束 K extends keyof T,这是关键。

假设继续使用上面的 User 接口:

  1. 约束 第二参数:K extends keyof T 意味着 K 必须是 T 的属性名联合类型的子集。例如,K 可以是 "id""id" | "name",但不能是 "email"(因为 User 里没有这个属性)。
  2. 遍历 指定键:[P in K] 仅遍历传入的 K 中的键,而不是 T 的所有键。
  3. 赋值 原类型:T[P] 从原类型中取出对应的类型定义。

3. 实际应用场景

当你只需要一个对象的部分属性时,使用 Pick

// 只需要用户的 id 和 name
type UserPreview = Pick<User, "id" | "name">;
// 等价于: { id: number; name: string; }

function showUserPreview(user: UserPreview) {
    console.log(`ID: ${user.id}, Name: ${user.name}`);
}

核心差异对比

为了更直观地区分两者,请参考下表:

特性 Partial Pick
主要用途 将所有属性变为可选 从原有类型中提取指定属性
参数数量 1 个 (T) 2 个 (T, K)
对属性的影响 增加 ? 修饰符 保持原有修饰符(只读、可选等)
生成属性数量 等于 T 的属性总数 等于 K 的属性总数

进阶理解:映射修饰符

在 TypeScript 中,除了 ?(可选),还有 readonly(只读)修饰符。PartialPick 的实现都利用了映射类型的修饰符机制。

Partial 中,[P in keyof T]? 的语法实际上是给属性添加了 +?+ 号表示添加,- 号表示移除。

尝试 编写一个 Required 类型(将可选属性变回必选),这是 Partial 的反向操作:

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

这里的 -? 表示移除可选性。


总结

通过源码可以发现,这两个工具类型并没有复杂的魔法,它们都基于以下简单的逻辑公式:

对于 Partial:遍历 T 的所有键,并在键后加上问号。

$$ \text{Partial} = \{ P \in \text{Keys}(T) \Rightarrow P?: T[P] \} $$

对于 Pick:遍历 KT 的键子集),保持键不变。

$$ \text{Pick}(T, K) = \{ P \in K \Rightarrow P: T[P] \} $$

掌握 keyofin 和索引访问 T[P] 这三板斧,你就可以根据自己的需求,随意组合出类似 DeepPartial(深度可选)或 Omit(排除属性)等高级工具类型。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文