泛型是 TypeScript 类型系统里最核心、也最考验功力的概念——它让类型"参数化",写一次代码就能适配多种类型。这篇把泛型从头讲清楚:为什么要泛型、泛型函数/接口/约束、以及最常用的工具类型。
一、解决什么问题:不写死类型
没有泛型,想写一个"返回原值"的函数,要么用 any(丢类型),要么为每种类型各写一个:
typescript
// 用 any:丢失类型信息,调用方拿不到类型提示
function identity(x: any): any { return x; }
const r = identity("hello"); // r 是 any,没有类型提示
// 泛型:类型参数化,返回类型和入参类型一致
function identity<T>(x: T): T { return x; }
const r1 = identity("hello"); // r1 是 string(自动推断)
const r2 = identity(18); // r2 是 number泛型 = 类型的"参数"。<T> 声明一个类型参数,调用时自动推断(或手动指定)。
二、泛型函数与泛型接口
typescript
// 泛型函数:多个类型参数
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
pair("age", 18); // [string, number]
// 泛型接口:最常见的是"响应包装"
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
// 不同接口复用同一个包装
const userResp: ApiResponse<User> = { code: 0, message: "ok", data: { name: "BinMaker" } };
const listResp: ApiResponse<User[]> = { code: 0, message: "ok", data: [...] };泛型接口是后端返回类型、状态管理里的标配——一个 ApiResponse<T> 包所有返回结构。
三、泛型约束:限制类型参数
有时要限制 T 必须有某些属性,用 extends 约束:
typescript
// 约束 T 必须有 length 属性
function getLen<T extends { length: number }>(x: T): number {
return x.length;
}
getLen("hello"); // ✓ string 有 length
getLen([1, 2, 3]); // ✓ 数组有 length
// getLen(18); // ❌ number 没有 length,编译报错<T extends 约束> 的语义:T 必须是约束类型的子类型(满足约束的结构)。
四、工具类型:TS 内置的泛型利器
TS 内置了几个最常用的工具类型,都是泛型的应用:
typescript
interface User {
name: string;
age: number;
email: string;
}
// Partial:所有字段变可选(常用于更新场景)
type PartialUser = Partial<User>; // { name?; age?; email?; }
// Pick:挑出部分字段
type NameOnly = Pick<User, "name">; // { name: string }
// Omit:排除部分字段(常用!去掉密码等敏感字段)
type SafeUser = Omit<User, "email">; // { name; age; }
// Record:构造"键值对"类型
type UserMap = Record<string, User>; // { [key: string]: User }Omit 特别常用:接口返回时排除敏感字段(如 Omit<User, "password">),一行搞定。
小结
- 泛型 = 类型参数化,写一次适配多种类型
- 泛型函数/接口让类型"可复用",
ApiResponse<T>是经典场景 <T extends 约束>限制类型参数必须有某结构- 工具类型:Partial(全可选)、Pick(挑字段)、Omit(排除字段,常用)、Record(键值对)
泛型是 TS 高级类型(条件类型、映射类型、类型推断)的基础,理解了泛型,看那些高级类型就顺了。想回顾 TS 类型系统的其他基础,看《TypeScript 类型系统》。
