TypeScript 常用工具类型对比速查
你需要的是一份TypeScript常用工具类型的对比速查表,要求能直观看清Extract、Exclude、Pick、Omit等工具类型的区别、用法和核心源码实现,并且不用表格格式呈现。
下面我为你整理了最常用的TypeScript工具类型速查表,按功能分类并附上核心实现和使用示例,方便你快速对比:
一、集合筛选类(基于联合类型)
1. Exclude
-
作用:从联合类型T中排除可以赋值给类型U的成员
-
核心源码:
type Exclude<T, U> = T extends U ? never : T; -
使用示例:
// 从 "a" | "b" | "c" 中排除 "a" | "b",得到 "c" type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c" // 从 number | string | boolean 中排除 number,得到 string | boolean type T2 = Exclude<number | string | boolean, number>; // string | boolean
2. Extract
-
作用:从联合类型T中提取可以赋值给类型U的成员(与Exclude相反)
-
核心源码:
type Extract<T, U> = T extends U ? T : never; -
使用示例:
// 从 "a" | "b" | "c" 中提取 "a" | "b",得到 "a" | "b" type T1 = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b" // 从 number | string | boolean 中提取 string | boolean,得到 string | boolean type T2 = Extract<number | string | boolean, string | boolean>; // string | boolean
二、对象属性操作类
1. Pick
-
作用:从对象类型T中挑选指定的属性K(K必须是T的属性)
-
核心源码:
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; -
使用示例:
interface User { id: number; name: string; age: number; email: string; } // 只挑选 id 和 name 属性 type UserBasicInfo = Pick<User, "id" | "name">; // 结果:{ id: number; name: string; }
2. Omit
-
作用:从对象类型T中排除指定的属性K(与Pick相反)
-
核心源码:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; -
使用示例:
interface User { id: number; name: string; age: number; email: string; } // 排除 age 和 email 属性 type UserBasicInfo = Omit<User, "age" | "email">; // 结果:{ id: number; name: string; }
三、类型修饰类
1. Partial
-
作用:将对象类型T的所有属性变为可选
-
核心源码:
type Partial<T> = { [P in keyof T]?: T[P]; }; -
使用示例:
interface User { id: number; name: string; } // 所有属性变为可选 type PartialUser = Partial<User>; // 结果:{ id?: number; name?: string; }
2. Required
-
作用:将对象类型T的所有属性变为必选(与Partial相反)
-
核心源码:
type Required<T> = { [P in keyof T]-?: T[P]; }; -
使用示例:
interface User { id?: number; name?: string; } // 所有属性变为必选 type RequiredUser = Required<User>; // 结果:{ id: number; name: string; }
3. Readonly
-
作用:将对象类型T的所有属性变为只读
-
核心源码:
type Readonly<T> = { readonly [P in keyof T]: T[P]; }; -
使用示例:
interface User { id: number; name: string; } // 所有属性变为只读 type ReadonlyUser = Readonly<User>; // 结果:{ readonly id: number; readonly name: string; }
四、映射/转换类
1. Record
-
作用:创建一个对象类型,键类型为K,值类型为T
-
核心源码:
type Record<K extends keyof any, T> = { [P in K]: T; }; -
使用示例:
// 键为 "a" | "b",值为 number 类型 type MyRecord = Record<"a" | "b", number>; // 结果:{ a: number; b: number; } // 常用场景:定义字典类型 type UserMap = Record<string, { id: number; name: string }>; // 结果:{ [key: string]: { id: number; name: string; } }
2. ReturnType
-
作用:提取函数类型T的返回值类型
-
核心源码:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; -
使用示例:
function getUser() { return { id: 1, name: "张三" }; } // 提取 getUser 函数的返回值类型 type User = ReturnType<typeof getUser>; // 结果:{ id: number; name: string; }
3. Parameters
-
作用:提取函数类型T的参数类型,返回一个元组类型
-
核心源码:
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; -
使用示例:
function login(username: string, password: string) { return true; } // 提取 login 函数的参数类型 type LoginParams = Parameters<typeof login>; // 结果:[username: string, password: string]
总结
-
集合筛选:
Exclude(排除)和Extract(提取)用于联合类型的成员筛选,逻辑相反; -
对象属性:
Pick(挑选)和Omit(排除)用于对象属性的选择,Omit基于Pick和Exclude实现; -
核心逻辑:所有工具类型都基于TypeScript的泛型、条件类型(
T extends U ? X : Y)、索引类型(keyof T)和映射类型([P in K])实现,infer关键字用于类型推导。 这个写法是 TypeScript 中Object.entries()方法的类型定义,我们来逐部分解析:
entries<T>(o: { [s: string]: T; } | ArrayLike<T>): [string, T][];
🔍 一、整体含义
这是对一个函数(比如 Object.entries)的类型签名定义,表示:
函数
entries接收一个参数o,它可以是:
- 一个对象(键为字符串,值为类型
T),或- 一个类数组(ArrayLike)结构(如数组、arguments 等)
返回一个 由
[key, value]组成的二维数组,每个元素是[string, T]类型。
🧩 二、逐段解释
1. entries<T>
- 这是一个泛型函数,
T是泛型参数,代表“值”的类型。 - 比如:
entries<string>表示返回的是字符串值的键值对。
2. o: { [s: string]: T; } | ArrayLike<T>
-
参数
o的类型是两种类型的联合:①
{ [s: string]: T; }- 表示一个对象,其属性名是字符串,属性值是类型
T - 例如:
{ a: 1, b: 2 }→T是number
②
ArrayLike<T>- 表示类似数组的对象,比如普通数组、
arguments对象等 - 它必须有
length属性和数字索引 - 例如:
[1, 2, 3]或arguments
- 表示一个对象,其属性名是字符串,属性值是类型
✅ 所以这个函数可以处理对象和类数组(数组)
3. : [string, T][]
- 返回值是一个数组,数组中每个元素是
[string, T]形式的元组 - 即每项是一个包含两个元素的数组:
- 第一个是
string类型(键) - 第二个是
T类型(值)
- 第一个是
✅ 示例说明
const obj = { a: 1, b: 2 };
const arr = [10, 20, 30];
// 假设 entries 是实现该签名的函数
const result1 = entries(obj); // [[ "a", 1 ], [ "b", 2 ]]
const result2 = entries(arr); // [[ "0", 10 ], [ "1", 20 ], [ "2", 30 ]]
注意:实际
Object.entries()不支持ArrayLike,但有些库或自定义工具会扩展支持。所以这里的ArrayLike<T>可能是为了兼容性设计。
⚠️ 注意事项
Object.entries()在标准 JavaScript 中 只支持对象,不支持数组(它会把数组当作对象处理,但不会遍历数值索引)- 如果你真的想对数组做
entries风格处理,建议使用Array.prototype.entries()或手动转换
// 正确用法
Object.entries({ a: 1, b: 2 }) // => [ ['a', 1], ['b', 2] ]
// 错误用法(虽然语法不报错,但结果可能不符合预期)
Object.entries([1, 2]) // => [ ['0', 1], ['1', 2] ]
📌 总结
| 部分 | 含义 |
|---|---|
entries<T> | 泛型函数,T 是值的类型 |
{ [s: string]: T } | 字符串键、T 类型值的对象 |
ArrayLike<T> | 类数组(如数组) |
[string, T][] | 返回 [键, 值] 的二维数组 |
👉 这种写法常见于 自定义工具函数 或 TypeScript 工具类型 中,用来增强类型安全性。
如果你看到这个定义是在某个库中,可能是为了扩展 Object.entries 的功能,支持更多数据结构。
[s: string] 是 TypeScript 中 索引签名(Index Signature) 的语法,用于描述对象中通过字符串键访问属性的类型规则。
🔍 一、基本语法
{ [keyName: string]: ValueType }
keyName:只是一个占位符名称(如s、key、prop),没有实际作用,可以任意命名。string:表示属性的键(key)是string类型。ValueType:表示所有通过该键访问到的值(value)必须是这个类型。
✅ 所以
[s: string]中的s只是一个形式参数名,真正重要的是: string表示“键是字符串”。
🧩 二、作用:定义“任意字符串键 → 某类型值”的对象
例如:
type StringRecord = { [s: string]: number };
这表示:
一个对象,任何字符串作为键,对应的值都必须是
number。
合法用例:
const obj: StringRecord = {
age: 25, // ✅ 'age' 是 string 键,值是 number
score: 90 // ✅
};
非法用例:
const bad: StringRecord = {
name: "Alice" // ❌ 报错!"Alice" 是 string,但值必须是 number
};
🆚 对比:Record<string, T> vs { [s: string]: T }
这两者在大多数情况下等价:
type A = { [s: string]: number };
type B = Record<string, number>;
// A 和 B 几乎完全一样
Record<K, V>是 TypeScript 内置工具类型,更简洁、语义更清晰。- 索引签名写法更底层,有时用于复杂场景(如混合固定属性 + 索引签名)。
⚠️ 注意事项
-
键只能是
string或number(或symbol){ [k: boolean]: string } // ❌ 错误!键不能是 boolean -
number索引会隐式兼容string索引(但反过来不行){ [n: number]: string } // 允许 obj[0], 也允许 obj["0"](因为 "0" 可转为数字) -
索引签名会覆盖所有未显式声明的属性
type User = { name: string; // 固定属性 [prop: string]: any; // 其他任意属性 };
💡 为什么用 s?可以换名字吗?
完全可以!以下写法等价:
{ [key: string]: number }
{ [prop: string]: number }
{ [anything: string]: number }
{ [s: string]: number } // s 只是习惯简写(可能是 "string key" 的缩写)
TypeScript 只关心 : string 部分,前面的名字会被忽略。
✅ 总结
| 写法 | 含义 |
|---|---|
[s: string] | 索引签名,表示“任意字符串作为键” |
s | 无意义的占位符,可任意命名 |
| 实际约束 | 所有通过字符串键访问的值必须符合指定类型 |
这是 TypeScript 中定义“动态键对象”的核心语法之一。