Skip to Content

面试导航 - 程序员面试题库大全 | 前端后端面试真题 | 面试

TypescriptTS 中的泛型

泛型(Generics)是 TypeScript 的一个强大功能,它允许我们在定义函数、类或接口时,使用类型参数来进行类型的约束。通过泛型,我们可以在保持类型安全的同时,实现代码的复用。

使用泛型的最大优势是能够让你编写可重用的代码,同时确保类型安全。假设你在写一个函数,它能处理多种类型的数据,但你不确定到底是哪种类型。使用泛型可以帮助你在不丧失类型检查的情况下,处理不同的数据类型。

泛型的基本用法

泛型函数

泛型函数允许你在函数定义时指定类型参数。比如:

function identity<T>(arg: T): T { return arg; } const num = identity(42); // num 的类型是 number const str = identity('Moment'); // str 的类型是 string

在上面的代码中:

  • T 是一个类型参数,它并不代表任何特定的类型。
  • identity 函数返回的类型与参数的类型相同。这里通过泛型保证了类型安全。

泛型数组

你可以使用泛型来定义数组的类型:

function logArray<T>(arr: T[]): void { arr.forEach((item) => console.log(item)); } logArray([1, 2, 3]); // 输出数字 logArray(['a', 'b', 'c']); // 输出字符串

在这里,T[] 表示 arr 是一个包含类型 T 的数组,T 可以是任何类型。

泛型接口

接口也可以使用泛型,通常用于定义那些在类型不确定时仍然能有效工作的接口:

interface Box<T> { value: T; } const numberBox: Box<number> = { value: 42 }; const stringBox: Box<string> = { value: 'Hello' };

在这个例子中,Box 是一个泛型接口,它定义了一个 value 属性,这个属性的类型由 T 来决定。

泛型类

你也可以在类中使用泛型:

class Container<T> { value: T; constructor(value: T) { this.value = value; } getValue(): T { return this.value; } } const numContainer = new Container(42); const strContainer = new Container('Moment'); console.log(numContainer.getValue()); // 42 console.log(strContainer.getValue()); // "Moment"

这里,Container 是一个泛型类,可以接收不同的类型。

泛型约束

有时你可能希望泛型类型参数能够满足某种条件,比如它必须是一个特定类型的子类。可以使用泛型约束来实现:

function logLength<T extends { length: number }>(item: T): void { console.log(item.length); } logLength([1, 2, 3]); // 输出 3 logLength('Hello'); // 输出 5 // 下面的代码会报错,因为 number 类型没有 `length` 属性 // logLength(123);

T extends { length: number } 这个约束表示,T 必须是一个具有 length 属性的类型。

如下图所示:

你也可以为泛型指定默认类型:

function wrap<T = string>(value: T): T { return value; } const wrapped = wrap(42); // 自动推断为 number 类型 const wrappedString = wrap('Hello'); // 使用默认的 string 类型

在这个例子中,如果没有显式指定 T 的类型,T 会默认为 string

多个泛型类型参数

一个函数或接口可以使用多个泛型参数:

function combine<T, U>(a: T, b: U): [T, U] { return [a, b]; } const result = combine(42, 'Hello'); // result 是 [number, string]

在上面的例子中,combine 函数使用了两个类型参数:TU

项目场景:API 请求库

假设你在开发一个 API 请求库,功能是发送 HTTP 请求并处理响应数据。因为 API 返回的数据格式会根据不同的接口而有所不同,因此你希望能够保证每次请求都能按照正确的类型来处理响应数据。

如下代码所示:

async function apiRequest<T>(url: string, method: string = 'GET', body: any = null): Promise<T> { const response = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : null, }); if (!response.ok) { throw new Error(`Request failed with status ${response.status}`); } // 假设 API 返回的 JSON 数据符合 T 类型 return response.json(); }

接下来我们使用泛型来发起请求并确保类型安全,如下代码所示:

interface User { id: number; name: string; } interface Product { id: number; title: string; price: number; } // 请求用户数据 async function fetchUserData(userId: number): Promise<User> { const url = `https://api.example.com/users/${userId}`; return apiRequest<User>(url); // 传入 User 类型作为泛型参数 } // 请求商品数据 async function fetchProductData(productId: number): Promise<Product> { const url = `https://api.example.com/products/${productId}`; return apiRequest<Product>(url); // 传入 Product 类型作为泛型参数 } async function main() { try { const user = await fetchUserData(1); console.log(user.id, user.name); // 类型安全,user 是 User 类型 const product = await fetchProductData(101); console.log(product.id, product.title, product.price); // 类型安全,product 是 Product 类型 } catch (error) { console.error(error); } } main();

在上面的代码中,apiRequest<T> 是一个泛型函数,用于发送 HTTP 请求并根据传入的泛型类型(如 UserProduct)确保返回的数据符合预期结构。通过泛型,TypeScript 可以在编译时检查响应数据的类型,提供类型安全,防止数据结构不匹配的问题。

如下所示:

总结

TypeScript 的泛型(Generics)允许我们在函数、类和接口中使用类型参数,从而实现代码的复用和类型安全。通过泛型,我们可以处理多种类型的数据,并确保返回值符合预期的结构。它支持类型约束、多个类型参数和默认类型,使得代码更加灵活和可维护。泛型在实际项目中尤其适用于处理动态类型的数据,如 API 请求库中确保响应数据的类型安全。

最后更新于:
Copyright © 2025Moment版权所有粤ICP备2025376666