引言:为什么没有泛型,TypeScript 会失去一半的力量?
想象一下,你是一位建筑师,每次都要从零开始设计一栋建筑,即使只是再建一个同样的车库。这大致就是开发者在没有使用 TypeScript 泛型 的情况下,为数字、字符串和数组编写相同函数时的感受。听起来很熟悉?如果你已经掌握了 TypeScript 的基本类型,但感觉编写了太多重复的代码,那么这篇文章就是为你准备的。
泛型 是一种机制,它允许创建能够处理不同类型(而非特定类型)的组件(函数、类、接口)。你只需编写一次代码,类型会自动代入,而无需复制代码。在本文中,我们将探讨 TypeScript 中泛型编程的工作原理,展示真实的代码示例,并提供实用建议,使你的代码更简洁、更安全。
我们是 PythonLib,今天讨论 TypeScript,因为没有它,现代前端开发将无法想象。开始吧!
TypeScript 中的泛型是什么,为什么需要它们?
让我们从一个简单的例子开始。假设你想编写一个返回传入参数的函数(恒等函数)。
// 没有泛型——不得不使用 anyfunction identity(arg: any): any { return arg;}
// 或者为每个类型编写重载function identityNumber(arg: number): number { return arg; }function identityString(arg: string): string { return arg; }第一种方式破坏了类型化——你丢失了类型信息。第二种方式则迫使你生成大量相同的函数。TypeScript 泛型 优雅地解决了这个问题:
function identity<T>(arg: T): T { return arg;}
const num = identity<number>(42); // 类型 numberconst str = identity('hello'); // 类型 string(类型推断)const bool = identity(true); // 类型 boolean符号 <T> 是类型参数。你可以随意命名(通常使用 T, U, V),但如果参数较多,最好使用有意义的名称。当你调用函数时,TypeScript 会用实际类型替换 T。如果未显式指定类型,编译器会根据参数自动推断。
因此,泛型类型 允许在之前不得不使用 any 的地方保持严格的类型化。这减少了 bug 的数量,并使代码具有自文档性。
泛型语法:从简单到复杂
泛型函数
我们已经看到了基本示例。让我们增加难度:一个接受数组并返回其第一个元素的函数。
function getFirst<T>(arr: T[]): T | undefined { return arr[0];}
const firstNumber = getFirst([1, 2, 3]); // number | undefinedconst firstString = getFirst(['a', 'b', 'c']); // string | undefined注意:T[] 是 Array<T> 的简写。你可以使用任何一种形式。
泛型接口
带有泛型的接口是描述数据结构的强大工具。例如,类型化的 API 响应:
interface ApiResponse<T> { data: T; status: number; message: string;}
// 使用const userResponse: ApiResponse<{ id: number; name: string }> = { data: { id: 1, name: 'Alice' }, status: 200, message: 'OK'};现在,一个接口可以描述用户、商品、订单的响应——只需传入相应的类型即可。
泛型类
类也可以是泛型的。经典示例是队列:
c