大家好,我是小杨。今天想和大家聊聊TypeScript中让我又爱又恨的一个特性------泛型。
记得我第一次接触泛型时,看着那一对尖括号<>,心里直犯嘀咕:这玩意儿到底有啥用?直到我在项目中写了这样的代码:
typescript
// 最初的我:使用any大法
function identity(value: any): any {
return value;
}
const result1 = identity("hello"); // result1的类型是any
const result2 = identity(42); // result2的类型也是any
看起来没问题,对吧?但用着用着就发现不对劲了------我完全失去了类型信息!result1和result2都变成了any,TypeScript的所有优势荡然无存。
泛型来救场了!
泛型就像是给函数、类或接口的一个"类型参数",让我们在使用时才确定具体的类型:
typescript
// 现在的我:使用泛型
function identity<T>(value: T): T {
return value;
}
const result1 = identity("hello"); // result1的类型是string!
const result2 = identity(42); // result2的类型是number!
看到了吗?TypeScript自动推断出了返回值的具体类型,这就是泛型的魔力!
泛型的几种实用场景
1. 处理数组不再头疼
typescript
// 以前处理数组很麻烦
function getFirstElement(arr: any[]): any {
return arr[0];
}
// 现在可以这样
function getFirstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const stringArray = ["apple", "banana", "cherry"];
const numberArray = [1, 2, 3];
const firstString = getFirstElement(stringArray); // 类型是string
const firstNumber = getFirstElement(numberArray); // 类型是number
2. 让接口也"通用"起来
typescript
// 一个通用的API响应接口
interface ApiResponse<T> {
code: number;
message: string;
data: T;
timestamp: Date;
}
// 用户数据
interface User {
id: number;
name: string;
email: string;
}
// 商品数据
interface Product {
id: number;
title: string;
price: number;
}
// 现在可以这样使用
const userResponse: ApiResponse<User> = {
code: 200,
message: "success",
data: {
id: 1,
name: "张三",
email: "zhangsan@example.com"
},
timestamp: new Date()
};
const productResponse: ApiResponse<Product> = {
code: 200,
message: "success",
data: {
id: 101,
title: "笔记本电脑",
price: 5999
},
timestamp: new Date()
};
3. 约束泛型:给类型加个"边界"
有时候我们希望泛型有一定的限制,不是随便什么类型都可以:
typescript
// 要求传入的对象必须有length属性
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): void {
console.log(`Length: ${item.length}`);
}
logLength("hello"); // ✅ 字符串有length
logLength([1, 2, 3]); // ✅ 数组有length
logLength({ length: 5 }); // ✅ 对象有length属性
// logLength(42); // ❌ 数字没有length属性
4. 多个泛型参数
typescript
// 处理键值对
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = {
name: "李四",
age: 30,
email: "lisi@example.com"
};
const personName = getProperty(person, "name"); // string类型
const personAge = getProperty(person, "age"); // number类型
// getProperty(person, "height"); // ❌ 没有height这个属性
我在实际项目中的泛型应用
让我分享一个在真实项目中很有用的例子:
typescript
// 分页查询的通用类型
interface PaginationParams {
page: number;
pageSize: number;
}
interface PaginationResult<T> {
data: T[];
total: number;
currentPage: number;
totalPages: number;
hasNext: boolean;
}
async function paginateQuery<T>(
params: PaginationParams,
fetchData: (params: PaginationParams) => Promise<T[]>,
countData: () => Promise<number>
): Promise<PaginationResult<T>> {
const [data, total] = await Promise.all([
fetchData(params),
countData()
]);
return {
data,
total,
currentPage: params.page,
totalPages: Math.ceil(total / params.pageSize),
hasNext: params.page < Math.ceil(total / params.pageSize)
};
}
// 使用示例
interface Article {
id: number;
title: string;
content: string;
}
// 查询文章列表
const articleResult = await paginateQuery<Article>(
{ page: 1, pageSize: 10 },
fetchArticles,
countArticles
);
// articleResult.data 的类型是 Article[]
我的心得体会
泛型学习有个过程,我总结了几点经验:
- 从简单开始 :先理解
<T>的基本概念,不要一开始就追求复杂的高级用法 - 多用多练:在合适的场景刻意使用泛型,慢慢就会找到感觉
- 适度使用:不是所有地方都需要泛型,在真正需要通用性的地方使用它
- 善用约束 :用
extends给泛型加上合理的限制,避免滥用
泛型就像是TypeScript中的"万能钥匙",一旦掌握,就能写出既灵活又类型安全的代码。它让我们的类型系统从"死板"变得"智能",这才是TypeScript真正的威力所在!
希望这篇分享能帮你打开泛型的大门。如果你在学习过程中有什么心得体会,欢迎在评论区交流讨论!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!