使用 yapi-to-typescript 自动生成 TypeScript 类型文件

866 阅读2分钟
yapi-to-typescript

QQ截图20230630114158.png

yapi-to-typescript 是一个代码生成工具,其可根据 YApi 或 Swagger 的接口定义生成 TypeScript 或 JavaScript 的接口类型及其请求函数代码

安装

选择你常用的包管理器将 yapi-to-typescript 加入项目依赖即可:

# npm
npm i yapi-to-typescript

# yarn
yarn add yapi-to-typescript

# pnpm
pnpm add yapi-to-typescript

配置

首先, 在项目使用以下命令初始化配置文件:

 npx ytt init

然后你的项目会生成一个 ytt.config.tsytt.config.js

以下是 ytt.config.tsytt.config.js 的配置

import { defineConfig } from 'yapi-to-typescript';

const tokenList = [
  {
    // 名称
    name: 'name',
    // 对应的token 获取方式:打开项目 -> 设置 -> token 配置 -> 复制 token
    token: '',
    categoryIds: [0],
  }
]

// 项目中的能力可能不止一个, 所以得用循环去拿各个能力得配置
export default defineConfig(
  tokenList.map((v) => ({
    // 配置服务器地址
    serverUrl: "https://yapi.xxxxxx.com", // 这里是你yapi的地址
    // 是否只生成接口请求内容和返回内容的 TypeSript 类型,是则请求文件和请求函数都不会生成。
    typesOnly: false,
    // 要生成的目标代码类型
    target: 'typescript',
    // 支持生成 React Hooks 代码的相关配置。
    reactHooks: {
      // 是否开启该项功能。
      enabled: false,
    },
    // 生产环境名称。用于获取生产环境域名。
    prodEnvName: 'production',
    // 输出文件路径。可以是 相对路径 或 绝对路径。如 'src/api/index.ts'
    outputFilePath: `src/services/${v.name}Api.ts`,
    // 请求函数文件路径。如 'src/api/request.ts'
    // 这里默认不选会在 outputFilePath 输出的目录生成一个默认的request
    requestFunctionFilePath: 'src/utils/request.ts', // 这里是改成用Axios配置的
    // 接口返回的数据形
    dataKey: 'data',
    // 项目列表
    projects: [
      {
        token: v.token,
        categories: [
          {
            id: v.categoryIds,
            getRequestFunctionName(interfaceInfo, changeCase) {
              return changeCase.camelCase(interfaceInfo.method + interfaceInfo.path)
            },
          },
        ],
      },
    ],
  }))
)

使用

配置好上面后使用以下命令生成代码:

npx ytt

执行完成后就会在配置 outputFilePath 的目录下生成文件了, 例如上面我配置的是: src/services/${v.name}Api.ts 就会在 src 目录下的 services 文件夹生成对应的代码

使用 Axios 统一请求函数示例

下面是一个基于 Axios 的示例, 可通过配置项的 requestFunctionFilePathrequest 指向你自己写的 request

# src/utils/request.ts

import Axios from "axios";

const basePrefix = '/api'
const BACK_GATEWAY = '/';

const defaultHeaders = {
  'Content-Type': 'Application/json',
  'token': '',
  'environment': '1',
  'source': '99',
  site: 1
}

const request = Axios.create({
  baseURL: BACK_GATEWAY,
  timeout: 30 * 1000,
  headers: defaultHeaders
})

// 添加请求拦截器
request.interceptors.request.use((config) => {
  // 判断是否有权限
  console.log("判断是否有权限: TOKEN")
  // 在发送请求之前做些什么
  return config
}, (error) => {
  // 对请求错误做些什么
  return Promise.reject(error)
});

// 添加响应拦截器
request.interceptors.response.use((response) => {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  return response
}, (error) => {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  return Promise.reject(error)

});

export interface IRequestSuccess<T> {
  code: number,
  data: T,
  message: string,
  time: number
}

class ApiRequest {
  // eslint-disable-next-line class-methods-use-this
  createRequest<T>(options: any): Promise<unknown> {
    return new Promise((resolve, reject) => {
      request<IRequestSuccess<T>>({ url: basePrefix + options.path, ...options }).then(_reslove => {
        const res = _reslove.data as IRequestSuccess<T>
        // 登录验证
        if (res.code === 1101) {
          return false
        }

        // 统一拦截 参数校验错误,显示后端返回的状态信息
        // @todo 这里不需要国际化
        if (res.code === 1102) {
          reject(res)
          return false
        }

        // 请求成功执行
        if (res.code === 1000) {
          resolve(res)
        } else {
          resolve(res)
        }
      }).catch((err) => {
        // http错误处理, 直接透传
        reject(err)
      })
    })
  }
}

export default new ApiRequest().createRequest;