← 返回首页
文章
2026-03-31
TypeScript 泛型完整教程
TypeScript 泛型完整教程 从零到精通,覆盖泛型函数、泛型接口、泛型类、泛型约束、实战应用等核心场景。 目录 一、核心概念 二、基础使用 三、进阶用法 四、实战场景 五、常见问题 六、总结速记 一、核心概念 1....
TypeScript 泛型完整教程 #
从零到精通,覆盖泛型函数、泛型接口、泛型类、泛型约束、实战应用等核心场景。
目录 #
一、核心概念 #
1.1 什么是泛型? #
大白话解释:
泛型就是"类型的模板"——让你写一个函数/类/接口,能适配多种类型,而不是只固定一种类型。
类比理解:
想象你去餐厅点餐:
- 固定类型:菜单只有"红烧肉",想吃别的?没有。
- 泛型:菜单写着"炒菜(可自选食材)",你想吃炒牛肉就选牛肉,想吃炒青菜就选青菜。
在 TypeScript 中:
- 固定类型:
function add(a: number, b: number)→ 只能加数字 - 泛型:
function add<T>(a: T, b: T)→ T 可以是 number、string、任何类型
1.2 为什么需要泛型? #
问题场景:写一个"返回输入值"的函数。
// 方案一:固定类型(只能用 number)
function identity(arg: number): number {
return arg;
}
identity(100); // ✅ OK
identity("hello"); // ❌ 报错:不能传字符串
// 方案二:用 any(丢失类型信息)
function identity(arg: any): any {
return arg;
}
identity(100); // 返回值类型是 any,不是 number
identity("hello"); // 返回值类型是 any,不是 string
// 问题:传入 number,返回值应该是 number,但 TS 不知道泛型解决方案:
function identity<T>(arg: T): T {
return arg;
}
identity<number>(100); // 返回值类型是 number ✅
identity<string>("hello"); // 返回值类型是 string ✅
// 更方便:自动推断类型
identity(100); // TS 自动推断 T = number
identity("hello"); // TS 自动推断 T = string1.3 泛型的核心名词 #
| 名词 | 解释 | 示例 |
|---|---|---|
| 泛型参数 | 用 <T> 定义的类型占位符 |
<T>、<K, V> |
| 类型变量 | 泛型参数的具体名称 | T(常用)、K(键)、V(值) |
| 泛型函数 | 带泛型参数的函数 | function fn<T>(arg: T): T |
| 泛型接口 | 带泛型参数的接口 | interface Container<T> { value: T } |
| 泛型类 | 哈泛型参数的类 | class Box<T> { content: T } |
| 泛型约束 | 限制泛型的范围 | <T extends string> |
| 类型推断 | TS 自动推断泛型类型 | identity(100) → 自动推断 T = number |
二、基础使用 #
2.1 泛型函数 #
最简单的泛型函数:
// 定义泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 使用方式一:显式指定类型
const num = identity<number>(100); // num 类型是 number
const str = identity<string>("hello"); // str 类型是 string
// 使用方式二:自动推断类型(推荐)
const num2 = identity(100); // TS 自动推断 T = number
const str2 = identity("hello"); // TS 自动推断 T = string多泛型参数:
// 两个泛型参数
function swap<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
const result = swap("name", "张三"); // result 类型是 [string, string]
const result2 = swap(1, 100); // result2 类型是 [number, number]箭头函数泛型:
// 箭头函数写法
const identity = <T>(arg: T): T => arg;
// 多参数箭头函数
const createPair = <K, V>(key: K, value: V): [K, V] => [key, value];2.2 泛型接口 #
定义泛型接口:
// 泛型接口:容器
interface Container<T> {
value: T;
getValue(): T;
}
// 使用
const numberContainer: Container<number> = {
value: 100,
getValue() { return this.value; }
};
const stringContainer: Container<string> = {
value: "hello",
getValue() { return this.value; }
};泛型函数类型接口:
// 定义泛型函数类型
interface GenericFunction<T> {
(arg: T): T;
}
// 实现
const identity: GenericFunction<number> = (arg) => arg;
identity(100); // ✅ OK
identity("hello"); // ❌ 报错:类型不匹配2.3 泛型类 #
定义泛型类:
// 泛型类:盒子
class Box<T> {
private content: T;
constructor(content: T) {
this.content = content;
}
getContent(): T {
return this.content;
}
setContent(content: T): void {
this.content = content;
}
}
// 使用
const numberBox = new Box<number>(100);
numberBox.getContent(); // 返回 number
numberBox.setContent(200); // ✅ OK
numberBox.setContent("hi"); // ❌ 报错:类型不匹配
const stringBox = new Box<string>("hello");
stringBox.getContent(); // 返回 string泛型类的实际应用:
// 泛型类:存储器
class Storage<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getAll(): T[] {
return this.items;
}
find(predicate: (item: T) => boolean): T | undefined {
return this.items.find(predicate);
}
}
// 使用:存用户
interface User {
id: number;
name: string;
}
const userStorage = new Storage<User>();
userStorage.add({ id: 1, name: "张三" });
userStorage.add({ id: 2, name: "李四" });
userStorage.find(u => u.id === 1); // 返回 User | undefined2.4 泛型数组 #
// 泛型数组函数
function getFirst<T>(arr: T[]): T | undefined {
return arr[0];
}
getFirst([1, 2, 3]); // 返回 number | undefined
getFirst(["a", "b", "c"]); // 返回 string | undefined
getFirst([]); // 返回 undefined
// 泛型数组方法
function mapArray<T, U>(arr: T[], fn: (item: T) => U): U[] {
return arr.map(fn);
}
const numbers = [1, 2, 3];
const doubled = mapArray(numbers, n => n * 2); // doubled 类型是 number[]
const strings = mapArray(numbers, n => n.toString()); // strings 类型是 string[]三、进阶用法 #
3.1 泛型约束 #
问题:泛型太自由,访问不存在属性会报错。
function getLength<T>(arg: T): number {
return arg.length; // ❌ 报错:T 可能没有 length 属性
}解决:用 extends 约束泛型必须有某些属性。
// 定义约束接口
interface HasLength {
length: number;
}
// 泛型约束:T 必须有 length 属性
function getLength<T extends HasLength>(arg: T): number {
return arg.length; // ✅ OK
}
// 使用
getLength("hello"); // ✅ string 有 length
getLength([1, 2, 3]); // ✅ array 有 length
getLength({ length: 10 }); // ✅ 对象有 length 属性
getLength(100); // ❌ 报错:number 没有 length常用约束类型:
| 约束 | 说明 | 示例 |
|---|---|---|
T extends string |
T 必须是 string 或其子类型 | function fn<T extends string>(arg: T) |
T extends number |
T 必须是 number 或其子类型 | function fn<T extends number>(arg: T) |
T extends object |
T 必须是对象类型 | function fn<T extends object>(arg: T) |
T extends { prop: Type } |
T 必须有指定属性 | function fn<T extends { id: number }>(arg: T) |
3.2 keyof 约束(获取对象的属性) #
问题:写一个函数获取对象的某个属性值,但要确保属性名存在。
function getProperty(obj: any, key: string): any {
return obj[key]; // ❌ 不安全,key 可能不存在
}解决:用 keyof 约束 key 必须是对象的属性名。
// keyof 约束:K 必须是 T 的属性名之一
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// 使用
interface User {
id: number;
name: string;
age: number;
}
const user: User = { id: 1, name: "张三", age: 25 };
getProperty(user, "id"); // 返回 number ✅
getProperty(user, "name"); // 返回 string ✅
getProperty(user, "email"); // ❌ 报错:User 没有 email 属性3.3 泛型默认类型 #
// 泛型默认类型:如果不指定,就用默认类型
interface Container<T = string> {
value: T;
}
// 不指定类型,默认是 string
const strContainer: Container = { value: "hello" };
// 指定类型
const numContainer: Container<number> = { value: 100 };实际应用:API 响应类型。
// API 响应泛型,默认 data 是 unknown
interface ApiResponse<T = unknown> {
code: number;
message: string;
data: T;
}
// 不知道 data 类型时
const response: ApiResponse = {
code: 200,
message: "success",
data: null // data 类型是 unknown
};
// 知道 data 类型时
interface User {
id: number;
name: string;
}
const userResponse: ApiResponse<User> = {
code: 200,
message: "success",
data: { id: 1, name: "张三" } // data 类型是 User
};3.4 泛型与条件类型 #
// 条件类型:根据泛型类型返回不同结果
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
type C = IsString<"hello">; // "yes"(字符串字面量也是 string 子类型)实际应用:提取函数返回类型。
// 提取函数的返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function getUser(): { id: number; name: string } {
return { id: 1, name: "张三" };
}
type UserReturn = ReturnType<typeof getUser>; // { id: number; name: string }3.5 泛型工具类型(TypeScript 内置) #
TypeScript 提供了多个泛型工具类型:
| 工具类型 | 说明 | 示例 |
|---|---|---|
Partial<T> |
所有属性变可选 | Partial<User> |
Required<T> |
所有属性变必选 | Required<User> |
Readonly<T> |
所有属性变只读 | Readonly<User> |
Pick<T, K> |
选取部分属性 | `Pick<User, 'id' |
Omit<T, K> |
排除部分属性 | Omit<User, 'age'> |
Record<K, V> |
创建对象类型 | Record<string, number> |
ReturnType<T> |
获取函数返回类型 | ReturnType<typeof fn> |
使用示例:
interface User {
id: number;
name: string;
age: number;
email: string;
}
// Partial:所有属性可选
type PartialUser = Partial<User>;
// { id?: number; name?: string; age?: number; email?: string; }
// Pick:选取部分属性
type UserNameAndId = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }
// Omit:排除部分属性
type UserWithoutEmail = Omit<User, 'email'>;
// { id: number; name: string; age: number; }
// Record:创建键值对类型
type UserMap = Record<number, User>;
// { [key: number]: User }四、实战场景 #
场景 1:API 请求封装 #
需求:封装一个通用的 fetch 函数,返回类型自动推断。
// 泛型 API 请求函数
async function fetchApi<T>(url: string): Promise<T> {
const response = await fetch(url);
const data = await response.json();
return data as T;
}
// 定义响应类型
interface UserResponse {
id: number;
name: string;
email: string;
}
interface ProductResponse {
id: number;
title: string;
price: number;
}
// 使用:自动推断返回类型
const user = await fetchApi<UserResponse>('/api/user/1');
console.log(user.name); // ✅ TS 知道 name 是 string
const products = await fetchApi<ProductResponse[]>('/api/products');
products.forEach(p => console.log(p.title)); // ✅ TS 知道 title 是 string场景 2:表单验证 #
需求:通用表单验证器,支持不同表单字段类型。
// 定义验证规则类型
interface ValidationRule<T> {
required?: boolean;
minLength?: number; // 仅用于 string
min?: number; // 仅用于 number
max?: number; // 仅用于 number
pattern?: RegExp; // 仅用于 string
validator?: (value: T) => boolean;
}
// 泛型表单字段
interface FormField<T> {
value: T;
rules: ValidationRule<T>[];
error?: string;
}
// 验证函数
function validateField<T>(field: FormField<T>): boolean {
for (const rule of field.rules) {
if (rule.required && !field.value) {
field.error = "此字段必填";
return false;
}
// 其他验证规则...
}
return true;
}
// 使用:登录表单
interface LoginForm {
username: FormField<string>;
password: FormField<string>;
}
const loginForm: LoginForm = {
username: {
value: "",
rules: [
{ required: true },
{ minLength: 3, validator: v => v.length >= 3 }
]
},
password: {
value: "",
rules: [
{ required: true },
{ minLength: 6 }
]
}
};
validateField(loginForm.username); // ✅ 泛型验证场景 3:状态管理(简化版 Redux) #
需求:通用状态管理器,支持不同状态类型。
// 泛型状态管理器
class StateManager<T> {
private state: T;
private listeners: ((state: T) => void)[] = [];
constructor(initialState: T) {
this.state = initialState;
}
getState(): T {
return this.state;
}
setState(newState: T | ((prev: T) => T)): void {
if (typeof newState === 'function') {
this.state = (newState as Function)(this.state);
} else {
this.state = newState;
}
this.listeners.forEach(listener => listener(this.state));
}
subscribe(listener: (state: T) => void): () => void {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
}
// 使用:用户状态
interface UserState {
user: { id: number; name: string } | null;
loading: boolean;
error: string | null;
}
const userStore = new StateManager<UserState>({
user: null,
loading: false,
error: null
});
// 订阅状态变化
userStore.subscribe(state => {
console.log('用户状态更新:', state);
});
// 更新状态
userStore.setState({ user: { id: 1, name: "张三" }, loading: false, error: null });
// 函数式更新
userStore.setState(prev => ({
...prev,
loading: true
}));场景 4:事件发射器 #
需求:通用事件系统,支持不同事件类型和数据。
// 泛型事件类型
type EventHandler<T> = (data: T) => void;
// 泛型事件发射器
class EventEmitter<EventMap extends Record<string, any>> {
private handlers: Map<keyof EventMap, EventHandler<any>[]> = new Map();
on<K extends keyof EventMap>(event: K, handler: EventHandler<EventMap[K]>): void {
const existing = this.handlers.get(event) || [];
this.handlers.set(event, [...existing, handler]);
}
emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {
const handlers = this.handlers.get(event) || [];
handlers.forEach(handler => handler(data));
}
off<K extends keyof EventMap>(event: K, handler: EventHandler<EventMap[K]>): void {
const handlers = this.handlers.get(event) || [];
this.handlers.set(event, handlers.filter(h => h !== handler));
}
}
// 使用:定义事件类型
interface AppEvents {
'user:login': { userId: number; name: string };
'user:logout': { userId: number };
'message:send': { to: number; content: string };
'notification': { type: 'success' | 'error'; message: string };
}
const emitter = new EventEmitter<AppEvents>();
// 监听事件(类型安全)
emitter.on('user:login', data => {
console.log(`用户登录: ${data.name}`); // data 类型自动推断
});
emitter.on('notification', data => {
if (data.type === 'success') {
console.log(`成功: ${data.message}`);
}
});
// 发射事件
emitter.emit('user:login', { userId: 1, name: "张三" });
emitter.emit('notification', { type: 'success', message: "操作成功" });
// 错误示例:类型不匹配会报错
emitter.emit('user:login', { name: "张三" }); // ❌ 缺少 userId
emitter.emit('notification', { type: 'info', message: "..." }); // ❌ type 不是 'success' | 'error'场景 5:组件 Props 类型(React) #
需求:React 组件 Props 类型复用。
// 基础 Props
interface BaseProps {
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
}
// 泛型组件 Props
interface ListProps<T> extends BaseProps {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
onItemClick?: (item: T) => void;
}
// 泛型组件
function List<T>({ items, renderItem, onItemClick, className }: ListProps<T>) {
return (
<ul className={className}>
{items.map((item, index) => (
<li
key={index}
onClick={() => onItemClick?.(item)}
>
{renderItem(item, index)}
</li>
))}
</ul>
);
}
// 使用:用户列表
interface User {
id: number;
name: string;
avatar: string;
}
<List<User>
items={[
{ id: 1, name: "张三", avatar: "/avatar1.jpg" },
{ id: 2, name: "李四", avatar: "/avatar2.jpg" }
]}
renderItem={(user) => (
<div>
<img src={user.avatar} alt={user.name} />
<span>{user.name}</span>
</div>
)}
onItemClick={(user) => console.log('点击:', user.id)}
/>
// 使用:商品列表
interface Product {
id: number;
title: string;
price: number;
}
<List<Product>
items={[{ id: 1, title: "商品A", price: 100 }]}
renderItem={(product) => (
<div>
<h3>{product.title}</h3>
<p>¥{product.price}</p>
</div>
)}
/>五、常见问题 #
Q1:什么时候用泛型,什么时候用 any? #
| 情况 | 选择 | 原因 |
|---|---|---|
| 需要保留类型信息 | 泛型 | identity<T> 返回值类型与参数一致 |
| 真的不关心类型 | any | JSON.parse 返回值真的不确定 |
| 可以是多种类型 | 泛型 + 约束 | `<T extends string |
// 泛型:保留类型信息
function identity<T>(arg: T): T {
return arg;
}
const num = identity(100); // num 是 number
// any:丢失类型信息
function identityAny(arg: any): any {
return arg;
}
const numAny = identityAny(100); // numAny 是 any,不是 numberQ2:泛型参数命名规范? #
| 常用名称 | 说明 | 使用场景 |
|---|---|---|
T |
Type(类型) | 单泛型参数 |
K |
Key(键) | 对象键 |
V |
Value(值) | 对象值、Map |
E |
Element(元素) | 数组元素 |
R |
Return(返回) | 函数返回值 |
U |
第二个类型 | 多泛型参数 |
// 单泛型
function identity<T>(arg: T): T {}
// 对象键值
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {}
// Map 类型
interface Map<K, V> {
get(key: K): V;
set(key: K, value: V): void;
}
// 函数返回值
type ReturnType<T extends (...args: any[]) => any> =
T extends (...args: any[]) => infer R ? R : never;Q3:泛型可以嵌套吗? #
可以,但要注意类型复杂度。
// 泛型嵌套
interface Container<T> {
value: T;
}
interface NestedContainer<T> {
outer: Container<T>;
inner: Container<Container<T>>;
}
const nested: NestedContainer<string> = {
outer: { value: "outer" },
inner: { value: { value: "inner" } }
};
// 复杂嵌套(避免过度使用)
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};Q4:如何在箭头函数中使用泛型? #
注意 .tsx 文件中 <T> 可能被误认为是 JSX 标签。
// .ts 文件:正常写
const identity = <T>(arg: T): T => arg;
// .tsx 文件:加 extends 避免歧义
const identity = <T extends unknown>(arg: T): T => arg;
// 或
const identity = <T,>(arg: T): T => arg; // 加逗号区分Q5:泛型约束和继承的区别? #
| 概念 | 说明 | 示例 |
|---|---|---|
| 泛型约束 | 限制泛型类型范围 | <T extends string> |
| 接口继承 | 扩展接口定义 | interface A extends B |
// 泛型约束:限制 T 必须是 string 或其子类型
function fn<T extends string>(arg: T): T {
return arg;
}
fn("hello"); // ✅ OK
fn(100); // ❌ 报错:number 不满足约束
// 接口继承:扩展接口
interface Base { id: number; }
interface User extends Base { name: string; }
// User = { id: number; name: string; }六、总结速记 #
| 类型 | 核心要点 |
|---|---|
| 概念 | 泛型 = 类型模板,适配多种类型 |
| 语法 | <T> 定义泛型参数 |
| 函数 | function fn<T>(arg: T): T |
| 接口 | interface Container<T> { value: T } |
| 类 | class Box<T> { content: T } |
| 约束 | <T extends HasLength> 限制泛型范围 |
| keyof | <K extends keyof T> 确保 key 存在 |
| 工具类型 | Partial<T>、Pick<T, K>、Omit<T, K>、Record<K, V> |
附录 #
A. 泛型语法速查 #
// 泛型函数
function fn<T>(arg: T): T {}
const fn = <T>(arg: T): T => arg;
// 泛型接口
interface Container<T> { value: T; }
// 泛型类
class Box<T> { content: T; }
// 泛型约束
function fn<T extends { length: number }>(arg: T): number {
return arg.length;
}
// keyof 约束
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {}
// 默认类型
interface ApiResponse<T = unknown> { data: T; }
// 多泛型
function map<T, U>(arr: T[], fn: (item: T) => U): U[] {}B. 泛型工具类型速查 #
| 工具类型 | 说明 | 示例 |
|---|---|---|
Partial<T> |
所有属性可选 | Partial<User> |
Required<T> |
所有属性必选 | Required<User> |
Readonly<T> |
所有属性只读 | Readonly<User> |
Pick<T, K> |
选取属性 | Pick<User, 'id' | 'name'> |
Omit<T, K> |
排除属性 | Omit<User, 'email'> |
Record<K, V> |
键值对类型 | Record<string, number> |
ReturnType<T> |
函数返回类型 | ReturnType<typeof fn> |
Parameters<T> |
函数参数类型 | Parameters<typeof fn> |
NonNullable<T> |
排除 null/undefined | NonNullable<string | null> |
C. 推荐资源 #
| 名称 | 链接 | 说明 |
|---|---|---|
| TypeScript 官方文档 | https://www.typescriptlang.org/docs/handbook/2/generics.html | ✅ 官方泛型教程 |
| TypeScript Handbook | https://www.typescriptlang.org/docs/handbook/ | ✅ 完整手册 |
| TypeScript Playground | https://www.typescriptlang.org/play | ✅ 在线练习 |
| TypeScript Deep Dive | https://basarat.gitbook.io/typescript/ | ✅ 深入教程 |
最后更新:2026-03-28