如何用TypeScript结合Axios实现强类型REST客户端?
基于WorkoutLibrary接口的Axios实现示例
前置准备
- 安装Axios依赖:
npm install axios
- 补充定义接口中用到的辅助类型(需根据实际API结构调整):
// 统一错误响应类型 export interface ErrorResponse { code: number; message: string; } // 获取训练列表的入参 export interface GetWorkoutListOptions { page?: number; limit?: number; } // 获取训练列表的成功响应 export interface GetWorkoutList200Response { workouts: Array<{ id: string; name: string; duration: number }>; total: number; } // 创建训练的入参 export interface CreateWorkoutOptions { name: string; duration: number; exercises: Array<string>; } // 创建训练的成功响应 export interface CreateWorkout201Response { id: string; name: string; createdAt: string; } // 更新训练的入参 export interface UpdateWorkoutOptions { id: string; name?: string; duration?: number; } // 更新训练的成功响应 export interface UpdateWorkout200Response { id: string; name: string; updatedAt: string; } // 获取单个训练的成功响应 export interface GetWorkout200Response { id: string; name: string; duration: number; exercises: Array<string>; } // 删除训练的入参 export interface DeleteWorkoutOptions { workoutId: string; } // 删除训练的成功响应 export interface DeleteWorkout200Response { success: boolean; message: string; } // 你的原始接口(修正了get方法的入参类型错误) export interface WorkoutLibrary { "/Workout": { get: (input?: GetWorkoutListOptions) => Promise<GetWorkoutList200Response | ErrorResponse>; post: (input: CreateWorkoutOptions) => Promise<CreateWorkout201Response | ErrorResponse>; put: (input: UpdateWorkoutOptions) => Promise<UpdateWorkout200Response | ErrorResponse>; }; "/workout/{workoutId}": { get: (input?: { workoutId: string }) => Promise<GetWorkout200Response | ErrorResponse>; delete: (input?: DeleteWorkoutOptions) => Promise<DeleteWorkout200Response | ErrorResponse>; }; }
实现WorkoutApiService类
import axios, { AxiosError } from 'axios'; export class WorkoutApiService implements WorkoutLibrary { // 基础API域名,根据实际情况修改 private baseUrl = 'https://your-api-domain.com/api'; // 通用请求封装,统一处理错误 private async request<T>(config: Parameters<typeof axios.request>[0]): Promise<T | ErrorResponse> { try { const response = await axios.request<T>(config); return response.data; } catch (error) { if (axios.isAxiosError(error)) { const axiosError = error as AxiosError<ErrorResponse>; return { code: axiosError.response?.status || 500, message: axiosError.response?.data.message || axiosError.message || '请求失败' }; } return { code: 500, message: '未知错误' }; } } // 实现"/Workout"下的所有方法 "/Workout": { get: async (input?: GetWorkoutListOptions) => { return this.request<GetWorkoutList200Response>({ method: 'GET', url: `${this.baseUrl}/Workout`, params: input }); }, post: async (input: CreateWorkoutOptions) => { return this.request<CreateWorkout201Response>({ method: 'POST', url: `${this.baseUrl}/Workout`, data: input }); }, put: async (input: UpdateWorkoutOptions) => { return this.request<UpdateWorkout200Response>({ method: 'PUT', url: `${this.baseUrl}/Workout`, data: input }); } }; // 实现"/workout/{workoutId}"下的所有方法 "/workout/{workoutId}": { get: async (input?: { workoutId: string }) => { if (!input?.workoutId) { return { code: 400, message: 'workoutId 必填' }; } const url = `${this.baseUrl}/workout/${input.workoutId}`; return this.request<GetWorkout200Response>({ method: 'GET', url }); }, delete: async (input?: DeleteWorkoutOptions) => { if (!input?.workoutId) { return { code: 400, message: 'workoutId 必填' }; } const url = `${this.baseUrl}/workout/${input.workoutId}`; return this.request<DeleteWorkout200Response>({ method: 'DELETE', url }); } }; }
使用示例
// 初始化服务实例 const workoutService = new WorkoutApiService(); // 获取训练列表 async function fetchWorkouts() { const result = await workoutService["/Workout"].get({ page: 1, limit: 10 }); if ('code' in result) { console.error('获取列表失败:', result.message); } else { console.log('训练列表:', result.workouts); } } // 创建新训练 async function addWorkout() { const result = await workoutService["/Workout"].post({ name: '上肢力量训练', duration: 45, exercises: ['俯卧撑', '引体向上'] }); if ('code' in result) { console.error('创建失败:', result.message); } else { console.log('创建成功,训练ID:', result.id); } } // 获取单个训练详情 async function fetchWorkoutDetail() { const result = await workoutService["/workout/{workoutId}"].get({ workoutId: '123' }); if ('code' in result) { console.error('获取详情失败:', result.message); } else { console.log('训练详情:', result); } }
核心概念指引
- TypeScript接口契约:
WorkoutLibrary定义了API服务的方法、入参和返回值规范,强制实现类遵循规则,避免类型错误。 - Axios通用封装:
request方法统一处理请求逻辑和错误捕获,减少重复代码,同时将Axios错误转换为统一格式,方便上层处理。 - 路径参数替换:对于带占位符的路径,需要将
{workoutId}替换为实际参数值,确保请求URL正确。 - 联合类型窄化:通过
'code' in result判断返回值是成功响应还是错误响应,这是TypeScript区分联合类型的常用技巧。
内容的提问来源于stack exchange,提问作者Claudio Medrano
相关产品推荐
相关产品推荐

