HTTP 客户端在 TypeScript 中:正确发起请求
在现代 Web 开发中,处理 HTTP 请求是一项关键任务。TypeScript 作为 JavaScript 的强类型超集,为开发者提供了创建可靠且可预测的 HTTP 客户端的强大工具。本文将探讨如何正确组织 TypeScript 中的 HTTP 客户端,使您的代码安全、可读且易于维护。
为什么 TypeScript 是 HTTP 客户端的理想选择?
TypeScript 添加了静态类型检查,这在处理外部 API 时尤为重要。您可以确切知道服务器将返回哪些数据,并在编译阶段预防许多错误。来看一个基本示例:
// 不好:无类型的 JavaScriptconst data = await fetch('/api/users');const users = await data.json();// 我们不知道 users 的结构
// 好:带类型的 TypeScriptinterface User { id: number; name: string; email: string;}
const response = await fetch('/api/users');const users: User[] = await response.json();// 现在 users 具有清晰的结构在 TypeScript 中创建基础 HTTP 客户端
我们从创建一个通用的 HTTP 客户端开始,它将处理所有类型的请求。使用原生的 fetch API,该 API 在现代浏览器和 Node.js 18+ 中可用。
带类型检查的基础实现
interface HttpClientConfig { baseURL: string; headers?: Record<string, string>; timeout?: number;}
interface ApiResponse<T> { data: T; status: number; ok: boolean;}
class HttpClient { private baseURL: string; private headers: Record<string, string>; private timeout: number;
constructor(config: HttpClientConfig) { this.baseURL = config.baseURL; this.headers = { 'Content-Type': 'application/json', ...config.headers, }; this.timeout = config.timeout || 10000; }
private async request<T>( endpoint: string, options: RequestInit = {} ): Promise<ApiResponse<T>> { const url = `${this.baseURL}${endpoint}`; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout);
try { const response = await fetch(url, { ...options, headers: { ...this.headers, ...options.headers }, signal: controller.signal, });
clearTimeout(timeoutId);
const data: T = await response.json();
return { data, status: response.status, ok: response.ok, }; } catch (error) { clearTimeout(timeoutId); throw this.handleError(error); } }
private handleError(error: unknown): Error { if (error instanceof DOMException && error.name === 'AbortError') { return new Error('Request timeout'); } if (error instanceof TypeError) { return new Error('Network error'); } return error as Error; }
async get<T>(endpoint: string): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'GET' }); }
async post<T>(endpoint: string, body: unknown): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'POST', body: JSON.stringify(body), }); }
async put<T>(endpoint: string, body: unknown): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'PUT', body: JSON.stringify(body), }); }
async delete<T>(endpoint: string): Promise<ApiResponse<T>> { return this.request<T>(endpoint, { method: 'DELETE' }); }}使用客户端
// 创建客户端实例const api = new HttpClient({ baseURL: 'https://api.