TypeScript 中的泛型(Generics)是一种创建可复用代码 的方式,它允许我们在定义函数、接口或类时不预先指定具体的类型,而是在使用时传入类型参数,从而在保持类型安全的同时获得极大的灵活性。
一、泛型的基本概念
可以把泛型理解为"类型变量"。就像函数参数可以接受不同的值,泛型参数可以接受不同的类型 。
用一对尖括号 <> 声明类型变量,通常用 T(Type)表示。
typescript
// 不使用泛型:只能用于 number,返回 number
function identityNumber(arg: number): number {
return arg;
}
// 使用泛型:接受任意类型,并返回相同类型
function identity<T>(arg: T): T {
return arg;
}
// 使用
const num = identity<number>(42); // 类型为 number
const str = identity<string>('hello'); // 类型为 string
// 也可以让编译器自动推断类型
const auto = identity(true); // 类型为 boolean
泛型保证了输入类型和输出类型之间的关联 ,避免了使用 any 导致的类型丢失。
二、泛型的基本语法
1. 泛型函数
typescript
// 多个类型参数
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const p = pair('hello', 10); // [string, number]
2. 泛型接口
typescript
interface Box<T> {
value: T;
getValue(): T;
}
const stringBox: Box<string> = {
value: 'hello',
getValue() { return this.value; }
};
3. 泛型类
typescript
class Stack<T> {
private items: T[] = [];
push(item: T) { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
}
const numberStack = new Stack<number>();
numberStack.push(1);
const top = numberStack.pop(); // number | undefined
4. 泛型约束(extends)
有时需要限制泛型参数必须符合某种结构,可以使用 extends 关键字。
typescript
interface Lengthwise {
length: number;
}
// T 必须具有 length 属性
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength('hello'); // OK,string 有 length
logLength([1, 2, 3]); // OK
// logLength(123); // Error,number 没有 length
5. 泛型默认值
typescript
interface Response<T = string> {
data: T;
code: number;
}
// 不传默认 string
const res1: Response = { data: 'ok', code: 200 };
// 也可以覆盖
const res2: Response<number> = { data: 123, code: 200 };
三、泛型的典型应用场景
1. 内置工具类型(Utility Types)
TypeScript 提供了大量基于泛型的工具类型,极大地提升了开发效率。
typescript
interface User {
id: number;
name: string;
age: number;
}
// Partial<T>:所有属性变为可选
type PartialUser = Partial<User>;
// Pick<T, K>:从 T 中挑选一组属性
type UserPreview = Pick<User, 'id' | 'name'>;
// Record<K, V>:构造以 K 为键,V 为值的对象类型
type PageInfo = Record<string, string>; // { [key: string]: string }
// Omit<T, K>:排除某些属性
type UserWithoutAge = Omit<User, 'age'>;
// Readonly<T>:所有属性变为只读
type ReadonlyUser = Readonly<User>;
2. 封装通用 API 请求
用泛型定义统一的请求响应结构,让每个接口的返回值都有精确的类型。
typescript
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
const res = await fetch(url);
return res.json();
}
// 使用
interface UserInfo {
id: number;
name: string;
}
const userRes = await fetchData<UserInfo>('/api/user');
console.log(userRes.data.name); // 有完整的类型提示
3. 前端状态管理(Pinia / Vuex)
定义 Store 时可以传入泛型,让 state、getters、actions 的类型自动推导。
typescript
// Pinia 示例
const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubleCount: (state) => state.count * 2
}
});
// defineStore 内部大量使用泛型,最终 store.count 是 number,doubleCount 是 number
4. 泛型组件(React / Vue3)
在 Vue3 的 <script setup> 或 React 函数组件中,可以用泛型定义灵活的 Props。
Vue3 示例:
typescript
// 定义泛型组件 Props
<script setup lang="ts" generic="T">
const props = defineProps<{
items: T[];
getKey: (item: T) => string;
}>();
</script>
<template>
<div v-for="item in items" :key="getKey(item)">
<slot :item="item" />
</div>
</template>
React 示例:
tsx
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
5. 高阶函数与柯里化
定义参数动态、类型关联的高阶函数时,泛型必不可少。
typescript
function map<T, U>(arr: T[], fn: (item: T, index: number) => U): U[] {
return arr.map(fn);
}
const lengths = map(['a', 'bc'], s => s.length); // number[]
6. 事件与表单处理
typescript
// 泛型事件处理函数
function handleChange<T extends HTMLInputElement | HTMLSelectElement>(
e: Event & { target: T }
) {
console.log(e.target.value); // 能正确识别 value
}
// 表单验证函数
function validate<T extends Record<string, any>>(data: T, rules: Record<keyof T, (val: any) => boolean>) {
// ...
}
7. 数据结构的通用实现
typescript
class Queue<T> {
private data: T[] = [];
enqueue(item: T) { this.data.push(item); }
dequeue(): T | undefined { return this.data.shift(); }
}
四、泛型的好处总结
| 优势 | 说明 |
|---|---|
| 类型安全 | 避免 any,在编译期捕获类型错误 |
| 代码复用 | 一套逻辑适用于多种类型,减少重复代码 |
| 抽象能力 | 让函数、组件能够与不同类型协作,而不丢失类型信息 |
| 智能推导 | IDE 可以根据传入参数自动推断泛型,减少显式标注,提升开发体验 |
| 可维护性 | 类型即文档,一眼就能看出函数期望的输入输出关系 |
泛型是 TypeScript 类型系统的灵魂,掌握它将使你从"使用类型"的开发者进阶为"设计类型"的开发者,写出既安全又优雅的代码。