初学ts,折腾了一下午,终于把返回值推断弄出来了. 封装请看 一篇文章带你详细了解axios的封装
主要思路是改造AxiosInstance,来源如何在axios拦截器中将类型向后传递? - 峎族的回答 - 知乎
下面直接上代码
axios封装文件
import axios, {
// AxiosInstance,
AxiosResponse,
AxiosRequestConfig,
InternalAxiosRequestConfig,
} from 'axios'
import { ElMessage, ElLoading } from 'element-plus'
// 返回值类型
export interface ResponseData<T = any> {
code?: number
msg?: string
token?: string
data?: T
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export interface Response<T = any> {
data: ResponseData
config: InternalAxiosRequestConfig
}
export interface AxiosInstance {
<T = any>(config: AxiosRequestConfig): Promise<T>
request<T = any>(config: AxiosRequestConfig): Promise<T>
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
head<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>
post<T = any>(
url: string,
data?: any,
config?: AxiosRequestConfig,
): Promise<T>
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
patch<T = any>(
url: string,
data?: any,
config?: AxiosRequestConfig,
): Promise<T>
interceptors: any
}
// 核心在这里,改造AxiosInstance使之返回我们想要的类型
// 我的接口固定返回ResponseData
// ResponseData里面的data又可以由调用接口时传入
export interface IAxiosInstance extends AxiosInstance {
<D = any>(config: AxiosRequestConfig): Promise<ResponseData<D>>
}
const loadingInstance = ElLoading.service
let requestCount = 0
const showLoading = () => {
requestCount++
if (requestCount === 1) loadingInstance()
}
const closeLoading = () => {
requestCount--
if (requestCount === 0) loadingInstance().close()
}
const service: IAxiosInstance = axios.create({
method: 'get',
baseURL: import.meta.env.VITE_APP_BASE_API,
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
timeout: 10000,
})
//请求拦截
declare module 'axios' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface InternalAxiosRequestConfig<D = any, T = any> {
loading?: boolean
isToken?: boolean
}
}
declare module 'axios' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface AxiosRequestConfig<D = any> {
loading?: boolean
isToken?: boolean
}
}
const requestMap = new Map()
service.interceptors.request.use(
(config: InternalAxiosRequestConfig<any>) => {
const controller = new AbortController()
const key = config.data + config.url
config.signal = controller.signal
if (requestMap.has(key)) {
requestMap.get(key).abort()
requestMap.delete(key)
} else {
requestMap.set(key, controller)
}
// console.log(123);
const { loading = true, isToken = true } = config
if (loading) showLoading()
if (localStorage.getItem('token') && !isToken) {
config.headers['Authorization'] =
'Bearer ' + localStorage.getItem('token') // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config
},
(error) => {
console.log(error)
},
)
service.interceptors.response.use(
(res: AxiosResponse<any, Response<ResponseData>>) => {
const { data, config } = res
const { loading = true } = config
if (loading) closeLoading()
if (data.code != 200) {
switch (data.code) {
case 500:
ElMessage({
message: data.msg,
type: 'error',
})
break
default:
ElMessage({
message: data.msg ?? '请求失败',
type: 'error',
})
}
if (data.code === 401) {
//登录状态已过期.处理路由重定向
console.log('loginOut')
}
throw new Error(data?.msg ?? '请求失败')
}
return data
},
(error) => {
closeLoading()
let { message } = error
if (message == 'Network Error') {
message = '后端接口连接异常'
} else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substring(message.length - 3) + '异常'
}
ElMessage({
message: message,
type: 'error',
})
return Promise.reject(error)
},
)
export const request = service
调用接口
import { request } from '@/utils/service'
interface IUserRequestData {
username: string
password: string
}
// 生成tag列表interface
export interface TagList {
color?: string
creator?: string
name?: string
_id?: string
}
/** 登录,返回 token */
export function accountLogin(data: IUserRequestData) {
return request({
url: 'users/login',
method: 'post',
data,
})
}
/** 获取tag列表 */
export function tagList(params?: any) {
return request<TagList[]>({
url: 'tags/list',
method: 'get',
params,
})
}
思考了一下,不替代原来的AxiosInstance,另一种方法:
//axios封装文件中
// 其他地方与上述一样
// 这里原样引入AxiosInstance,不改造
import axios, {
AxiosInstance,
} from 'axios'
//这里依旧
export interface IAxiosInstance extends AxiosInstance {
<D = any>(config: AxiosRequestConfig): Promise<ResponseData<D>>
}
const service: IAxiosInstance = axios.create({
method: 'get',
baseURL: import.meta.env.VITE_APP_BASE_API,
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
timeout: 10000,
})
//调用的时候request后面必须加上<>才能导向IAxiosInstance实例
// 没有规定类型的时候使用any,有类型使用自己的类型
/** 登录,返回 token */
export function accountLogin(data: IUserRequestData) {
return request<any>({
url: 'users/login',
method: 'post',
data,
})
}
/** 获取tag列表 */
export function tagList(params?: any) {
return request<TagList[]>({
url: 'tags/list',
method: 'get',
params,
})
}
调用的时候可以看到类型提示了
更新
// request.ts
这里使用Omit过滤掉AxiosInstance里面的request方法后,请求就会走下面的方法
export interface IAxiosInstance extends Omit<AxiosInstance, 'request'> {
<T = any, R = ResponseData<T>, D = any>(
config: AxiosRequestConfig<D>,
): Promise<R>
/**
* @description 用于请求数据
* @param T 指定返回值类型
*/
<T = any>(config?: AxiosRequestConfig): Promise<ResponseData<T>>
/**
* @description 用于请求数据
* @param T 指定返回值类型
* @param D 指定请求值类型
*/
<T = any, D = any>(config: AxiosRequestConfig<D>): Promise<ResponseData<T>>
}
// user调用
/** 登录,返回 token */
// 直接request就行,不用加上request<any>
export function accountLogin(data: IUserRequestData) {
return request({
url: 'users/login',
method: 'post',
data,
})
}