在TypeScript中如何为带响应拦截器的Axios搭配useSWR Fetcher正确编写类型?
在TypeScript中如何为带响应拦截器的Axios搭配useSWR Fetcher正确编写类型?
嘿,这个问题我之前在项目里踩过一模一样的坑!因为Axios默认的类型体系是围绕返回AxiosResponse<T>设计的,但咱们的响应拦截器直接返回了response.data,这就打破了原本的类型约定,导致useSWR没法正确推断接口返回的数据类型。咱们一步步来把它搞定:
第一步:给Axios实例重新定义匹配拦截器的类型
首先,我们需要修改Axios实例的类型,让它的所有请求方法(比如get、post、request)直接返回数据类型T,而不是默认的AxiosResponse<T>。
// api.ts import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; export const options = { baseURL: process.env.API_ORIGIN || "http://localhost:8080", withCredentials: true, }; // 自定义Axios实例类型,覆盖所有请求方法的返回值 type CustomAxiosInstance = { [K in keyof AxiosInstance]: AxiosInstance[K] extends <T = any>(...args: any[]) => Promise<AxiosResponse<T>> ? <T = any>(...args: Parameters<AxiosInstance[K]>) => Promise<T> : AxiosInstance[K]; }; // 创建实例并断言为我们自定义的类型 const API = axios.create(options) as CustomAxiosInstance; // 完善你的响应拦截器(补全错误处理逻辑) API.interceptors.response.use( (response) => response.data, // 直接返回数据,和我们的自定义类型完全匹配 (error) => { // 这里可以统一处理错误,比如返回更友好的自定义错误结构 return Promise.reject(error.response?.data || error.message); } ); export default API;
第二步:编写类型安全的useSWR Fetcher
有了类型正确的Axios实例后,我们就可以基于它创建一个符合useSWR要求的fetcher,同时给fetcher加上明确的类型约束:
// 可以把这个fetcher放在api.ts或者单独的swr-utils.ts文件里 import API from "./api"; import { AxiosRequestConfig } from "axios"; // 定义fetcher的通用类型:接收url和可选配置,返回对应数据的Promise type SWRFetcher = <T = any>(url: string, config?: AxiosRequestConfig) => Promise<T>; // 实现基础的GET请求fetcher export const fetcher: SWRFetcher = async (url, config = {}) => { return API.get(url, config); }; // 如果你需要支持多种请求方法(比如POST、PUT),可以写更灵活的fetcher: export const flexibleFetcher = async <T = any>( args: [string, AxiosRequestConfig?] | string, config?: AxiosRequestConfig ): Promise<T> => { const [url, reqConfig] = Array.isArray(args) ? args : [args, {}]; return API.request<T>({ url, ...reqConfig, ...config }); };
第三步:在useSWR中使用类型安全的fetcher
现在你在组件里使用useSWR时,就能让TypeScript正确推断数据类型了,也可以手动指定返回数据的类型,获得完整的代码提示:
import useSWR from "swr"; import { fetcher } from "./api"; // 先定义接口返回的数据类型 type UserProfile = { id: number; username: string; email: string; avatarUrl: string; }; const UserProfileComponent = () => { // 手动指定数据类型,TypeScript会自动校验fetcher的返回值是否匹配 const { data: userProfile, error } = useSWR<UserProfile>("/api/user/profile", fetcher); if (error) return <div>加载失败:{error}</div>; if (!userProfile) return <div>加载中...</div>; // 这里userProfile会被正确推断为UserProfile类型,所有属性都有类型提示 return ( <div> <h1>{userProfile.username}</h1> <p>邮箱:{userProfile.email}</p> </div> ); };
可选:给错误也加上类型
如果你的拦截器返回了自定义的错误结构,也可以给错误指定类型,让useSWR的error也有精准的类型提示:
// 定义自定义错误类型 type ApiError = { code: number; message: string; details?: string[]; }; // 修改拦截器的错误处理,确保返回的是ApiError格式 API.interceptors.response.use( (response) => response.data, (error) => { const apiError: ApiError = { code: error.response?.status || 500, message: error.response?.data?.message || error.message, details: error.response?.data?.details, }; return Promise.reject(apiError); } ); // 然后在useSWR中同时指定数据和错误类型 const { data: userProfile, error } = useSWR<UserProfile, ApiError>("/api/user/profile", fetcher); // 此时error会被推断为ApiError | undefined,访问error.code、error.message都有类型提示
这样一套下来,你的Axios实例、fetcher和useSWR的使用就都有完整的TypeScript类型支持了,不会再出现类型不匹配的警告或者错误。
备注:内容来源于stack exchange,提问作者Winfrey De Vera
相关产品推荐
相关产品推荐

