TS泛型:让类型也学会“套娃”,但这次很优雅

大家好,我是小杨。今天想和大家聊聊TypeScript中让我又爱又恨的一个特性------泛型。

记得我第一次接触泛型时,看着那一对尖括号<>,心里直犯嘀咕:这玩意儿到底有啥用?直到我在项目中写了这样的代码:

typescript 复制代码
// 最初的我:使用any大法
function identity(value: any): any {
    return value;
}

const result1 = identity("hello"); // result1的类型是any
const result2 = identity(42);      // result2的类型也是any

看起来没问题,对吧?但用着用着就发现不对劲了------我完全失去了类型信息!result1result2都变成了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[]

我的心得体会

泛型学习有个过程,我总结了几点经验:

  1. 从简单开始 :先理解<T>的基本概念,不要一开始就追求复杂的高级用法
  2. 多用多练:在合适的场景刻意使用泛型,慢慢就会找到感觉
  3. 适度使用:不是所有地方都需要泛型,在真正需要通用性的地方使用它
  4. 善用约束 :用extends给泛型加上合理的限制,避免滥用

泛型就像是TypeScript中的"万能钥匙",一旦掌握,就能写出既灵活又类型安全的代码。它让我们的类型系统从"死板"变得"智能",这才是TypeScript真正的威力所在!

希望这篇分享能帮你打开泛型的大门。如果你在学习过程中有什么心得体会,欢迎在评论区交流讨论!

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
GIS之路2 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug5 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu121387 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中29 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路33 分钟前
GDAL 实现矢量合并
前端
hxjhnct35 分钟前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星42 分钟前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端