TypeScript 常用工具类型对比速查

15 阅读7分钟

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]
    

总结

  1. 集合筛选Exclude(排除)和Extract(提取)用于联合类型的成员筛选,逻辑相反;

  2. 对象属性Pick(挑选)和Omit(排除)用于对象属性的选择,Omit基于PickExclude实现;

  3. 核心逻辑:所有工具类型都基于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 }Tnumber

    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:只是一个占位符名称(如 skeyprop),没有实际作用,可以任意命名。
  • 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 内置工具类型,更简洁、语义更清晰。
  • 索引签名写法更底层,有时用于复杂场景(如混合固定属性 + 索引签名)。

⚠️ 注意事项

  1. 键只能是 stringnumber(或 symbol

    { [k: boolean]: string } // ❌ 错误!键不能是 boolean
    
  2. number 索引会隐式兼容 string 索引(但反过来不行)

    { [n: number]: string } // 允许 obj[0], 也允许 obj["0"](因为 "0" 可转为数字)
    
  3. 索引签名会覆盖所有未显式声明的属性

    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 中定义“动态键对象”的核心语法之一。