← 返回导航

请求配置类型封装

业务类型 · 实战

场景说明

几乎每个前端项目都有一套 HTTP 请求封装,类似 axios 的请求配置:url、method、params、data、headers、timeout、responseType 等。用泛型约束响应数据类型,可以让请求函数返回的数据直接带有正确的类型,免去手动类型断言。同时,请求配置本身也需要类型化:method 用字面量联合约束、params/data 泛型化、headers 用 Record 定义结构,让整个请求链路类型安全闭环。

完整代码


      
// 请求配置类型封装:类似 axios 的请求配置 + 泛型响应类型
// method 字面量约束 + params/data 泛型 + responseType 联合

// 1. HTTP 方法字面量联合
type HttpMethod =
  | 'GET'
  | 'POST'
  | 'PUT'
  | 'DELETE'
  | 'PATCH'
  | 'HEAD'
  | 'OPTIONS';

// 2. 响应类型
type ResponseType =
  | 'json'
  | 'text'
  | 'blob'
  | 'arraybuffer'
  | 'document';

// 3. 请求头类型
type HeadersMap = Record<string, string | number | boolean | undefined>;

// 4. 请求配置:泛型 P=params, D=data
interface RequestConfig<P = any, D = any> {
  url: string;
  method?: HttpMethod;
  baseURL?: string;
  params?: P;                       // URL 查询参数
  data?: D;                         // 请求体数据
  headers?: HeadersMap;
  timeout?: number;                  // 超时时间,毫秒
  responseType?: ResponseType;
  withCredentials?: boolean;
  signal?: AbortSignal;             // 取消请求
}

// 5. 响应数据结构
interface ApiResponse<T = any> {
  data: T;
  status: number;
  statusText: string;
  headers: Record<string, string>;
  config: RequestConfig;
}

// 6. 业务统一响应(成功/失败联合)
type BizResponse<T> =
  | { code: 0; message: string; data: T; success: true }
  | { code: number; message: string; data: null; success: false };

// 7. 请求函数类型:泛型 T 约束响应 data 类型
interface RequestFunction {
  <T = any, P = any, D = any>(config: RequestConfig<P, D>): Promise<BizResponse<T>>;
  get<T = any, P = any>(url: string, config?: Omit<RequestConfig<P, never>, 'url' | 'method'>): Promise<BizResponse<T>>;
  post<T = any, D = any>(url: string, data?: D, config?: Omit<RequestConfig<never, D>, 'url' | 'method' | 'data'>): Promise<BizResponse<T>>;
}

// 8. 使用示例:用户 API
interface User {
  id: number;
  name: string;
  email: string;
}

interface UserListParams {
  page: number;
  pageSize: number;
  keyword?: string;
}

interface UserListData {
  list: User[];
  total: number;
}

// 声明请求函数(此处为类型示意,实现略)
declare const request: RequestFunction;

// GET 请求:params 受 UserListParams 约束
function fetchUsers(params: UserListParams) {
  return request.get<UserListData, UserListParams>('/api/users', { params });
}

// POST 请求:data 受 User 约束
function createUser(data: User) {
  return request.post<User, User>('/api/users', data);
}

关键点解析

使用示例与类型推导


      
// 响应数据类型自动推导
async function demo() {
  const res = await fetchUsers({ page: 1, pageSize: 10 });
  if (res.success) {
    res.data.list;    // User[] 类型,自动提示
    res.data.total;   // number 类型
  } else {
    res.data;         // null,无法访问 list/total
    res.message;      // string,错误信息
  }
}

// params 传错字段会被 TS 拦截
const badParams: UserListParams = {
  page: 1,
  pageSize: 10,
  // keywordd: 'a'  // 错误:keywordd 不存在于 UserListParams
};

// 自定义请求配置
const config: RequestConfig<UserListParams, never> = {
  url: '/api/users',
  method: 'GET',
  params: { page: 1, pageSize: 20 },
  timeout: 5000,
  responseType: 'json',
};

// 响应类型推导
type Res = BizResponse<User>;
// => { code: 0; message: string; data: User; success: true }
//  | { code: number; message: string; data: null; success: false }
在 Playground 中尝试