目录
[1. 属性修饰符:让类型"变个身"](#1. 属性修饰符:让类型“变个身”)
[Partial ------ 变"全员可选"](#Partial —— 变“全员可选”)
[Required ------ 变"全员必选"](#Required —— 变“全员必选”)
[Readonly ------ 变"只读"](#Readonly —— 变“只读”)
[2. 结构过滤:像筛子一样筛选类型](#2. 结构过滤:像筛子一样筛选类型)
[Pick ------ 只取我要的](#Pick —— 只取我要的)
[Omit ------ 排除我不要的](#Omit —— 排除我不要的)
[3. 函数类型侦探:提取函数的秘密](#3. 函数类型侦探:提取函数的秘密)
[Parameters ------ 提取参数类型](#Parameters —— 提取参数类型)
[ReturnType ------ 提取返回值类型](#ReturnType —— 提取返回值类型)
[4. 映射与构造:快速创建结构](#4. 映射与构造:快速创建结构)
[Record ------ 键值对工厂](#Record —— 键值对工厂)
[5. 进阶实战:手写自定义工具](#5. 进阶实战:手写自定义工具)
[实战 1:DeepPartial (深度可选)](#实战 1:DeepPartial (深度可选))
[实战 2:Get (点符号访问类型)](#实战 2:Get (点符号访问类型))
什么是泛型工具类型?
简单来说,泛型工具类型就是用来操作类型的函数。
普通函数是 Input -> Output(如 map 把数组转换成新数组)。
泛型工具类型是 Type -> Type(如 Partial 把所有属性变成可选)。
TypeScript 内置了大量开箱即用的工具类型,覆盖了前端开发 90% 的场景。
1. 属性修饰符:让类型"变个身"
这是最基础也是最高频的一类工具,主要用于修改对象属性的读写性或必选性。
Partial ------ 变"全员可选"
场景:当你有一个"编辑用户信息"的表单,用户可能只想修改昵称,不想改头像。你不需要把所有字段都重写一遍。
javascript
interface User {
id: number;
name: string;
age: number;
avatar: string;
}
// 场景:定义更新接口,不需要传 id,且其他字段都可选
type UserUpdateForm = Partial<Omit<User, 'id'>>;
// 等价于:
// type UserUpdateForm = {
// name?: string;
// age?: number;
// avatar?: string;
// }
function updateUser(id: number, data: UserUpdateForm) {
// ...
}
Required ------ 变"全员必选"
场景 :Partial 的反操作。比如从后端拿来的配置对象可能有缺省,但在运行时你确保它已经补全了,可以用它断言类型。


Readonly ------ 变"只读"
场景:Redux 的 State 或者 Vue/React 的 Props 定义,防止在组件内部意外修改父级传递的数据。
javascript
type ReadonlyState = Readonly<{
count: number;
}>;
// state.count = 2; // ❌ Error: Cannot assign to 'count' because it is read-only
2. 结构过滤:像筛子一样筛选类型
如果你只想从大接口中拿几个字段,或者剔除几个敏感字段,这一类工具是你的救星。
Pick<T, K> ------ 只取我要的
场景 :后端返回了一个包含 20 个字段的"用户详情",但你只需要渲染一个"用户卡片",只需要 id, name, avatar。
javascript
interface UserDetail {
id: number;
name: string;
email: string;
phone: string;
passwordHash: string; // 敏感信息
lastLoginIp: string;
}
// 只需要这三个字段做卡片展示
type UserCardProps = Pick<UserDetail, 'id' | 'name' | 'avatar'>;
Omit<T, K> ------ 排除我不要的
场景 :创建文章列表,数据结构和创建文章表单几乎一样,但列表不需要 content 字段(太长了),且不需要 id(新建时没有)。
javascript
interface Article {
id: number;
title: string;
content: string;
tags: string[];
}
// 新建文章表单:不需要 id
type CreateArticleDto = Omit<Article, 'id'>;
小技巧 :Omit 其实可以用 Pick 和 Exclude 组合实现,但 TS 内置了它,直接用更爽。
3. 函数类型侦探:提取函数的秘密
在编写高阶组件或装饰器时,我们经常需要知道一个函数"接收什么参数"以及"返回什么类型"。
Parameters ------ 提取参数类型
场景:你要写一个日志装饰器,包裹任意函数,打印它的参数名和值。
javascript
function log<T extends (...args: any[]) => any>(fn: T) {
return function(this: any, ...args: Parameters<T>) {
console.log('Function called with:', args);
return fn.apply(this, args);
};
}
function add(a: number, b: number) {
return a + b;
}
const wrappedAdd = log(add);
// wrappedAdd 的参数类型自动推断为
ReturnType ------ 提取返回值类型
场景:异步请求封装时,你想基于 API 函数的返回值定义 Redux 的 Action 类型。
javascript
async function fetchUser(id: number): Promise<{ name: string; age: number }> {
return { name: 'Jack', age: 18 };
}
// 自动推导 fetchUser 返回的 Promise 内部结构
type UserType = Awaited<ReturnType<typeof fetchUser>>;
// UserType = { name: string; age: number }
4. 映射与构造:快速创建结构
Record<Keys, Type> ------ 键值对工厂
场景:定义一个枚举对象,或者一个以 ID 为 Key 的字典。
javascript
// 定义一个角色权限映射
type Role = 'admin' | 'user' | 'guest';
type Permission = {
read: boolean;
write: boolean;
};
// 快速生成对象结构:所有角色的权限列表
type RolePermissions = Record<Role, Permission>;
const permissions: RolePermissions = {
admin: { read: true, write: true },
user: { read: true, write: false },
guest: { read: false, write: false },
};
5. 进阶实战:手写自定义工具
内置的很好用,但有时候我们需要更强大的功能。通过泛型递归和条件类型,我们可以自己造"轮子"。
实战 1:DeepPartial (深度可选)
内置的 Partial 只能处理一层。如果对象是嵌套的,我们需要递归地把所有层级的属性都变为可选。
javascript
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface Config {
server: {
host: string;
port: number;
};
db: {
name: string;
};
}
// 现在嵌套属性也是可选的了
type PartialConfig = DeepPartial<Config>;
// partialConfig.server?.host // 合法
实战 2:Get (点符号访问类型)
类似 Lodash 的 _.get,但是是在类型层面操作。根据字符串路径获取深层属性的类型。
javascript
type Get<T, P> = P extends `${infer K}.${infer Rest}`
? K extends keyof T
? Get<T[K], Rest>
: never
: P extends keyof T
? T[P]
: never;
interface ApiData {
user: {
info: {
name: string;
};
};
}
// 根据路径 'user.info.name' 获取类型 string
type UserNameType = Get<ApiData, 'user.info.name'>; // string