TypeScript 常用泛型工具函数
2026/7/1 14:31:44 网站建设 项目流程

目录

什么是泛型工具类型?

1. 属性修饰符:让类型“变个身”

Partial —— 变“全员可选”

Required —— 变“全员必选”

Readonly —— 变“只读”

2. 结构过滤:像筛子一样筛选类型

Pick —— 只取我要的

Omit —— 排除我不要的

3. 函数类型侦探:提取函数的秘密

Parameters —— 提取参数类型

ReturnType —— 提取返回值类型

4. 映射与构造:快速创建结构

Record —— 键值对工厂

5. 进阶实战:手写自定义工具

实战 1:DeepPartial (深度可选)

实战 2:Get (点符号访问类型)

参考官方文档:

什么是泛型工具类型?

简单来说,泛型工具类型就是用来操作类型的函数

普通函数是Input -> Output(如map把数组转换成新数组)。
泛型工具类型是Type -> Type(如Partial把所有属性变成可选)。

TypeScript 内置了大量开箱即用的工具类型,覆盖了前端开发 90% 的场景。

1. 属性修饰符:让类型“变个身”

这是最基础也是最高频的一类工具,主要用于修改对象属性的读写性或必选性。

Partial<T>—— 变“全员可选”

场景:当你有一个“编辑用户信息”的表单,用户可能只想修改昵称,不想改头像。你不需要把所有字段都重写一遍。

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<T>—— 变“全员必选”

场景Partial的反操作。比如从后端拿来的配置对象可能有缺省,但在运行时你确保它已经补全了,可以用它断言类型。

Readonly<T>—— 变“只读”

场景:Redux 的 State 或者 Vue/React 的 Props 定义,防止在组件内部意外修改父级传递的数据。

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

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(新建时没有)。

interface Article { id: number; title: string; content: string; tags: string[]; } // 新建文章表单:不需要 id type CreateArticleDto = Omit<Article, 'id'>;

小技巧Omit其实可以用PickExclude组合实现,但 TS 内置了它,直接用更爽。

3. 函数类型侦探:提取函数的秘密

在编写高阶组件或装饰器时,我们经常需要知道一个函数“接收什么参数”以及“返回什么类型”。

Parameters<T>—— 提取参数类型

场景:你要写一个日志装饰器,包裹任意函数,打印它的参数名和值。

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<T>—— 提取返回值类型

场景:异步请求封装时,你想基于 API 函数的返回值定义 Redux 的 Action 类型。

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 的字典。

// 定义一个角色权限映射 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只能处理一层。如果对象是嵌套的,我们需要递归地把所有层级的属性都变为可选。

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,但是是在类型层面操作。根据字符串路径获取深层属性的类型。

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

参考官方文档:

TypeScript Utility Types 官方文档

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询