
🙋 前言
本文仅作为TypeScript
泛型的一个使用记录,方便自己或读者复习使用方法。
😀 TypeScript 泛型是什么?
泛型是 TypeScript
的类型参数化工具 ,它允许开发者创建可重用的代码组件,这些组件可以处理多种类型 而不仅限于单一类型。其核心思想是在定义函数、接口或类时不预先指定具体类型 ,而是通过类型变量(如 T
、U
)作为占位符,在实际使用时才确定具体类型。
基本语法示例:
csharp
// T 是类型变量(类型参数)
function identity<T>(value: T): T {
return value;
}
// 使用显式类型指定
const strResult = identity<string>("Hello"); // 返回值类型为 string
// 使用类型推断
const numResult = identity(42); // 自动推断 T 为 number
泛型解决了使用 any
类型导致类型信息丢失的问题,在保持灵活性的同时确保类型安全。
🐂 泛型应用场景与解决问题
泛型主要解决两个核心问题:
- 代码复用:避免为不同类型编写重复逻辑
- 类型安全 :替代
any
类型,保持编译时类型检查
典型应用场景:
场景 | 问题描述 | 泛型解决方案 |
---|---|---|
集合类型 | 需要创建可容纳多种类型的数组/集合 | Array<T> |
API响应结构 | 不同API端点返回不同数据结构 | ApiResponse<T> |
工具函数 | 函数需要处理多种类型输入 | function reverse<T>(items: T[]) |
React组件 | 组件需要支持多种数据项类型 | List<T> 组件 |
实战示例:
typescript
// 场景1: 通用API响应结构
interface ApiResponse<T> {
success: boolean;
data: T;
timestamp: Date;
}
// 用户数据响应
type UserResponse = ApiResponse<{ id: number; name: string }>;
// 产品数据响应
type ProductResponse = ApiResponse<{ sku: string; price: number }>;
// 场景2: React泛型组件
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function GenericList<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
// 使用示例
<GenericList
items={users}
renderItem={user => <div>{user.name}</div>}
/>
💡 泛型编码优化与最佳实践
显式约束类型范围(extends
)
问题 :当操作泛型值的属性时,编译器无法确定属性是否存在
方案 :使用 extends
约束泛型类型范围
csharp
// ❌ 危险:可能访问不存在的属性
function getProperty<T>(obj: T, key: string): any {
return obj[key]; // 编译错误:类型T上不存在属性key
}
// ✅ 安全:约束K必须是T的键名
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]; // 安全访问
}
// 使用示例
const user = { name: "Alice", age: 30 };
const userName = getProperty(user, "name"); // string
// const userEmail = getProperty(user, "email"); // 错误:"email"不是user的键
优先使用类型推断
原则:充分利用TS类型推断能力,减少冗余代码
typescript
// ❌ 冗余:不必要的类型声明
const numbers: Array<number> = [1, 2, 3];
const result = mock<number>(42);
// ✅ 精简:让TS自动推断类型
const numbers = [1, 2, 3]; // 自动推断为number[]
const result = mock(42); // 自动推断T=number
// 复杂推断示例
function mapItems<T, U>(items: T[], mapper: (item: T) => U): U[] {
return items.map(mapper);
}
// 自动推断T=string, U=number
const lengths = mapItems(["a", "bb"], str => str.length); // number[]
避免多重泛型嵌套
问题 :过度复杂的泛型降低可读性和维护性
方案:拆分逻辑或使用工具类型
typescript
// ❌ 过度设计:三层泛型嵌套
function processDeep<
T extends object,
K extends keyof T,
V extends T[K]
>(obj: T, key: K, processor: (val: T[K]) => V) {
// 复杂实现...
}
// ✅ 简化:拆分为基础函数+工具类型
type ValueProcessor<T> = (val: T) => unknown;
function processValue<T>(
obj: T,
key: keyof T,
processor: ValueProcessor<T[keyof T]>
): T {
return { ...obj, [key]: processor(obj[key]) };
}
// 使用示例
const product = { name: "Laptop", price: 999 };
const discounted = processValue(product, "price", p => p * 0.9); // 价格打9折
工具类型简化泛型
目标:创建可复用的类型抽象,减少重复代码
ini
// ❌ 重复:相似结构重复定义
type User = { name: string; email: string };
type PartialUser = {
name?: string;
email?: string;
};
// ✅ 优雅:使用内置工具类型
type PartialUser = Partial<User>;
// ✅ 自定义工具类型
// 创建所有属性可为null的类型
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
// 使用示例
type User = { name: string; age: number };
type NullableUser = Nullable<User>;
/* 等价于:
{
name: string | null;
age: number | null;
}
*/
// 项目级工具类型库
// types.ts
export type WithId<T> = T & { id: string };
export type Paginated<T> = { items: T[]; total: number };
// 使用处
import { WithId } from "./types";
type ProductWithId = WithId<Product>;
🎯 总结:泛型使用的黄金法则
-
必要性原则:仅在需要类型灵活性时使用泛型,简单场景避免过度设计
-
安全边界 :始终使用
extends
约束泛型操作,防止运行时错误 -
简洁优先:
- 信任类型推断(90%场景不需显式指定泛型)
- 限制泛型参数数量(≤2个)
- 拆分复杂泛型逻辑
-
抽象复用:
- 使用内置工具类型(
Partial
,Pick
等) - 创建项目级工具类型库
- 使用内置工具类型(
-
性能认知:
- 泛型本身不影响运行时性能
- 关注编译时类型计算开销
- 避免破坏类型安全的强制断言