Swagger3.0 自动生成接口 TypeScript 类型声明

5,544 阅读3分钟

Swagger3.0 自动生成 TypeScript 类型声明

相关介绍

TypeScript 几乎已经可以应对前端领域所有的开发场景,让代码几乎可以达到全部的静态检查。但是,在前后端对接的情况下,却是有着不小的瑕疵,后端提供的接口往往很难直接转化为前端可用的类型声明。我们可以把接口理解成方法,所有的方法都没有办法准确高效的提供输入与输出的定义。

目前这部分的工作,大多都是人工编写以及维护,造成了资源浪费,维护成本高,错误率高的一些问题。解决这个问题,可以根据后端提供的接口信息,自动生成类型,甚至是接口调用方法。

以上说的情况在 restful 风格的接口中比较严重,因为没有规范,各个语言实现不一致。在 graphql 接口的场景下,这个问题已经有了比较好的解决方案。

所以本文主要说明的是 restful 接口如何自动生成类型以及调用方法,本人想到两种方案:

  1. 类似 Java 这种具有比较完善的反射特性的语言,是能够拿到每个接口的参数类型以及返回值类型,由此,可以得到每个接口的所有信息,可生成一个数据,提供给前端解析并转化为 TypeScript 类型声明文件。

    优点:前端接口定义全部由后端定义所生成;准确率极高;不会有人为错误;且可实时更新同步最新的接口信息。

    缺点:切换语言就需要重新实现,应用场景和扩展性不足;需要后端积极配合,编写反射相关代码。

  2. 通过第三方文档工具,比如 swagger,解析该文档工具生成的标准 json schema,生成每个接口的类型声明和调用方法

    优点:前端接口定义全部由文档定义所生成;准确率高;可实时更新同步最新的接口信息;跨语言,应用场景广泛。

    缺点:编写文档错误会导致生成的类型错误(该问题可使用 swagger 相关库可最大化减少错误);对文档要求比较严格,需要按照标准的文档规则编写;对开发人员要求比较高

这里采用了第二个方案,实现了一个基于 swagger3.0编写的脚本,实现了相关功能。

Swagger Json Schema 简要说明

对字段的定义

对一个字段的描述,大致分为了以下的 10 种情况:

  1. boolean

    { 
     "type": "number", 
     "description": "" // 字段说明
     "nullable": false // 是否可以为 null
    }
    
  2. integer

    { 
     "type": "integer", 
     "description": "" // 字段说明
    }
    
  3. number

    { 
     "type": "number", 
     "description": "" // 字段说明
    }
    
  4. string

    { 
     "type": "string", 
     "description": "" // 字段说明
    }
    
  5. array

    { 
     "type": "array", 
     "items": {} // 10中数据描述
    }
    
  6. object

    { 
     "type": "object", 
     "properties": {
    
     },
     "required": [] // 必填的字段
    }
    
  7. $ref

    { 
     "$ref": "" // 指向其他的类型
    }
    
  8. oneOf_allOf_anyOf

    { 
     "allOf": [{}] // 每一项为10中数据描述,指向其他的类型或直接定义
    }
    { 
     "oneOf": [{}] // 每一项为10中数据描述,指向其他的类型或直接定义
    }
    { 
     "anyOf": [{}] // 每一项为10中数据描述,指向其他的类型或直接定义
    }
    

遍历整个数据,递归整理,生成最终想要的类型文件。

生成结果

生成的文件即文档,提高了查看文档的效率

  1. 生成的目录结构与 swagger 显示一致,每个文件即一个分类

    api             // 输出到该文件夹
    ├── api.ts      // 所有字段的类型包含 query, param, body, response
    ├── common.ts   // 分类,内部含有调用文件
    └── user.ts
    
  2. api.ts 部分内容

    export type SubjectsByIdFormRvPatchBody = PatchSubjectBatchReqDto;
    /** 错误码 */
    export type SubjectsByIdFormRvPatchResponseErrorCodeUnion = '00000' | 'A0002';
    
    export interface SubjectsByIdFormRvPatchResponse {
         /** 错误码 */
         errorCode?: SubjectsByIdFormRvPatchResponseErrorCodeUnion;
         /** 错误消息 */
         message?: string;
    }
    
  3. common.ts 文件中接口调用方法部分内容## 改进

    import { request } from 'src/utils/request';
    import { StudysPostBody, StudysPostResponse } from './api'; 
    
    /** 创建项目 */
    export const studysPost = (body: StudysPostBody): Promise<StudysPostResponse> => request({ method: 'post', url: '/api/web/v1/studys', body }); 
    
    // ... 该分类下其他的接口调用方法
    

项目地址

github.com/amazecc/swa…