前言
从vue3出来后,使用率越来越高,同时由于vue3由ts编写,对ts支持非常好
越来越多人全面拥抱 vue3+ts 的组合
那么既然用到了ts,肯定就要用好,而不是把它当成 anyScript 来使用,那样就失去了ts的使用意义了
写ts的时候,由于各种类型限制,写起来是比较繁琐的
尤其是当我们在使用 axios 的时候,我们前端本地自己写的可以限制类型
那使用接口后返回的数据该怎么定义约束这些类型呢,让我们的项目更加规范
正文
实际上在axios中也是支持ts的,让我们把它提供的类型进行结合起来
这里我把我的实现思路代码以及注释贴出,以供参考
我们需要思考并实现这几个点
- 1、如何在请求拦截器的 请求头 上加上我们的 token类型,避免ts类型报错
- 2、怎么在接口返回数据时约束类型
- 3、怎么在调用接口时约束入参类型
首先我们按照正常项目思路,创建好我们的axios实例封装文件,在这里定义好相关的类型约束
创建一个 request.ts 文件并创建实例
import axios from 'axios'
/* 创建请求实例 */
const service = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 30000
})
然后我们需要改装一下axios自带的类型,并重新自定义为我们需要的类型
这里发现自定义的 RequestConfig 参数类型由于是在调用接口实例时使用的,所以headers非必传项
但是在拦截器器中返回的类型出了问题,请求拦截器中的参数必有 headers 字段,所以这里只能分开写成两个
// 引入 axios自带的 请求接口request参数类型 以及 请求接口headers头参数类型
import type { AxiosRequestConfig, AxiosRequestHeaders } from 'axios'
// 1、自定义请求接口headers头参数类型
type RequestHeader = AxiosRequestHeaders & { token?: string }
// 2、自定义请求接口request参数类型,可以加一些自己自定义的参数
interface RequestConfig extends AxiosRequestConfig {
headers?: RequestHeader // 放入请求头
noNeedToken?: boolean // 该接口是否需要token
}
interface RequestInterceptorsConfig extends RequestConfig { // 请求拦截器使用
headers: RequestHeader;
}
然后在请求拦截器里就可以把相关的自定义参数写上去了
/* 添加请求拦截器 */
service.interceptors.request.use(
(config: RequestInterceptorsConfig) => { // 请求拦截
if (config.noNeedToken) { // 自定义的参数,是否不需要token的接口
return config
}
const token = 'aaaaa' // 这里获取token
if (!token) {
return Promise.reject();
}
if (token) {
config.headers.token = token // 请求头中添加token
}
return config
},
(error: AxiosError) => { // 请求错误拦截
return Promise.reject(error);
}
);
ts的提示出现了,类型检查生效
接下来封装 request请求方法 ,约束接口返回数据类型,使用前面定义好的请求接口RequestConfig 类型
/* 封装实例的请求方法 */
// 传入泛型约束返回数据类型
// ApiResponse 主体后端返回格式
interface ApiResponse<T = any> {
code: number;
msg: string;
data: T; // 这里定义请求返回data数据类型
}
export default async function request<T>(config: RequestConfig) {
// axios实例的 request 接受的第一个泛型参数,就是返回数据data的类型
return service.request<ApiResponse<T>>(config).then((res) => res.data); // 返回axios的里data数据
}
这里的 ApiResponse 类型是在一般情况下,我们请求后回来的data数据还会再包裹一层后端定义的状态码、消息、数据,这才是我们需要的,所以也要加上类型
总算配置好了
接下来在scr下新建api文件夹,创建接口文件,引入这个axios实例并使用,直接在request方法传入后端返回的数据格式即可
import request from "../utils/request";
interface testModel {
name: string
age: number
}
export function test() {
return request<testModel>({
method: 'get',
url: '/test',
})
}
ts的提示出现了,类型检查生效
以后只需要根据后端接口文档定义好相应的返回数据类型,然后泛型传入quest方法即可
那么怎么约束入参类型呢,只需要在方法入口处限制即可
参数不对就会提示
配置完成!
下面贴上完整代码
代码
文件根目录/types/axios.d.ts
import type {
AxiosRequestConfig,
AxiosRequestHeaders,
AxiosError,
} from "axios";
// 自定义请求接口headers头参数类型
type RequestHeader = AxiosRequestHeaders & { token?: string }
// 自定义请求接口request参数类型,可以加一些自己自定义的参数
export interface RequestConfig extends AxiosRequestConfig {
headers?: RequestHeader // 放入请求头
noNeedToken?: boolean // 该接口是否需要token
}
export interface RequestInterceptorsConfig extends RequestConfig { // 请求拦截器使用
headers: RequestHeader;
}
// 主体后端返回格式
export interface ApiResponse<T = any> {
code: number;
msg: string;
data: T; // 这里定义请求返回data数据类型
}
文件根目录/src/utils/request.ts
import axios from "axios";
import type {
AxiosError,
} from "axios";
import type { RequestConfig, RequestInterceptorsConfig, ApiResponse } from '../../types/axios'
/* 创建请求实例 */
const service = axios.create({
baseURL: "xxxxxxxx",
timeout: 30000,
});
/* 添加请求拦截器 */
service.interceptors.request.use(
(config: RequestInterceptorsConfig) => { // 请求拦截
if (config.noNeedToken) { // 自定义的参数,是否不需要token的接口
return config
}
const token = 'aaaaa' // 这里获取token
if (!token) {
return Promise.reject();
}
if (token) {
config.headers.token = token
}
return config
},
(error: AxiosError) => { // 请求错误拦截
return Promise.reject(error);
}
);
/* 添加响应拦截器 */
service.interceptors.response.use();
/* 封装实例的请求方法 */
export default async function request<T>(config: RequestConfig) {
// axios实例的 request 接受的第一个泛型参数,就是返回数据data的类型
return service.request<ApiResponse<T>>(config).then((res) => res.data); // 返回axios的里data数据
}
文件根目录/src/api/user.ts
import request from "../utils/request";
interface testModel {
name: string
age: number
}
interface dataModel { // 接口入参类型
page: number
size: number
}
export function test(data: dataModel) {
return request<testModel>({
method: 'get',
url: '/test',
data
})
}
test({page: 1, size: 10}).then(res => {
console.log(res)
})