精彩在今天 發表於 2021-10-19 21:02:00

手摸手教你用 yapi-to-typescript生成Yapi的TypeScript数据类型

<h2>一 背景</h2>
<p>现代社会比较重视效率,本着这个思想宗旨,能用工具自动高效做的事情,就不要低质量的勤奋。yapi-to-typescript就是一款自动生成接口请求与响应的typescript数据类型定义的工具,可根据&nbsp;YApi&nbsp;或&nbsp;Swagger&nbsp;的接口定义生成 TypeScript 或 JavaScript 的接口类型及其请求函数代码。本文手把手教你在项目中把这个工具用起来,让加班时间少一点,摸鱼时间多一点。</p>
<h2>二&nbsp;安装配置</h2>
<h3>2.1&nbsp;在项目中安装yapi-to-typescript工具包</h3>
<div class="cnblogs_code">
<pre>yarn add -D yapi-to-typescript</pre>
</div>
<h3>2.2&nbsp;配置工具</h3>
<p>在项目根目录下创建yyt.config.ts配置文件</p>
<div class="cnblogs_code">
<pre>npx ytt init -c ytt.config.ts</pre>
</div>
<p>每个配置项的含义参见官方配置文档,将初始配置文件修改为:</p>
<div class="cnblogs_code">
<pre>import { defineConfig } from 'yapi-to-typescript'<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 生成Api接口名称Interface和ChangeCase数据类型参见node_modules\yapi-to-typescript\lib\esm\index.d.ts定义
* @param interfaceInfo : Interface
* @param changeCase:ChangeCase
* @returns 请求响应接口名称--pascal命名
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> genApiInterfaceName(interfaceInfo, changeCase) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 取解析路径dir最尾部的路径作为前缀路径</span>
const lastPath = interfaceInfo.parsedPath.dir.split('/'<span style="color: rgba(0, 0, 0, 1)">).pop();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 拼接前缀路径+文件名称</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> `${changeCase.pascalCase(lastPath)}${changeCase.pascalCase(interfaceInfo.parsedPath.name)}`;
}

export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> defineConfig([
{
    serverUrl: </span>'http://yapi.xxx.com'<span style="color: rgba(0, 0, 0, 1)">,
    typesOnly: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
    target: </span>'typescript'<span style="color: rgba(0, 0, 0, 1)">,
    reactHooks: {
      enabled: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
    },
    prodEnvName: </span>'项目名称'<span style="color: rgba(0, 0, 0, 1)">,
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将生成文件路径转化成小驼峰命名方式</span>
    outputFilePath: (interfaceInfo, changeCase) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 文件夹名称取api-url路径末尾2个</span>
      const filePathArr = interfaceInfo.path.split('/').slice(-2<span style="color: rgba(0, 0, 0, 1)">);
      const filePath </span>= filePathArr.map((item) =&gt; changeCase.camelCase(item)).join('/'<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> `src/types/httpTypes/${filePath}.ts`;
<span style="color: rgba(0, 0, 0, 1)">    },
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 生成ts文件中请求参数interface名称,将下划线命名转换成pascal命名</span>
    getRequestDataTypeName: (interfaceInfo, changeCase) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> `${genApiInterfaceName(interfaceInfo, changeCase)}Request`;
    },
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 生成ts文件中请求响应数据interface名称,将下划线命名转换成pascal命名</span>
    getResponseDataTypeName: (interfaceInfo, changeCase) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> `${genApiInterfaceName(interfaceInfo, changeCase)}Response`;
    },
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 响应数据中要生成ts数据类型的键名</span>
    dataKey: 'retdata'<span style="color: rgba(0, 0, 0, 1)">,
    projects: [
      {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> token获取方式: 在yapi-设置-token配置中查看</span>
      token: 'xxx-token'<span style="color: rgba(0, 0, 0, 1)">,
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分类id查找方式: 点击接口左侧的分类菜单,查看url地址栏最后面的数字获取</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分类id配置特别重要,配置错了无法生成对应的ts数据类型定义文件</span>
<span style="color: rgba(0, 0, 0, 1)">      categories: [
          {
            id: [</span>139], <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 批量加好友API分类id</span>
<span style="color: rgba(0, 0, 0, 1)">          },
      ],
      },
    ],
},
]);</span></pre>
</div>
<p>&nbsp;其中token和projects--categories--id的查看方式分别是:</p>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019191639755-1807183085.png"></p>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019205827165-719699197.png"></p>
<p>&nbsp;在package.json中添加指令</p>
<div class="cnblogs_code">
<pre>"scripts"<span style="color: rgba(0, 0, 0, 1)">: {
    </span>"ytt": "ytt"<span style="color: rgba(0, 0, 0, 1)">,
},</span></pre>
</div>
<h2>三 生成使用</h2>
<h3>3.1&nbsp;生成api接口请求与响应的ts数据类型</h3>
<div class="cnblogs_code">
<pre>yarn yyt</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019192516781-825578050.png"></p>
<p>&nbsp;注意:自动生成的ts数据类型文件,是不需要添加到git仓库中的,所以要在.gitignore中添加一条文件版本管理忽略规则</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 自动生成类型不允许上传
</span>/src/types/httpTypes/*</pre>
</div>
<h3>3.2 引用方式</h3>
<div class="cnblogs_code">
<pre>import type {AddClientToolsGetClientsRequest, AddClientToolsGetClientsResponse} from '@/types/httpTypes/addClientTools/getClients'<span style="color: rgba(0, 0, 0, 1)">;
interface IState {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 待处理|已处理tab</span>
<span style="color: rgba(0, 0, 0, 1)">activeTab: TTabVal;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 客户姓名</span>
<span style="color: rgba(0, 0, 0, 1)">clientName: string;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 展示列表</span>
<span style="color: rgba(0, 0, 0, 1)">showList: AddClientToolsGetClientsResponse;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 底部列表</span>
<span style="color: rgba(0, 0, 0, 1)">bottomList: AddClientToolsGetClientsResponse;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分页编号</span>
<span style="color: rgba(0, 0, 0, 1)">listNum: number;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 列表总记录数</span>
<span style="color: rgba(0, 0, 0, 1)">listTotal: number;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 加载请求是否结束</span>
loadEnd: <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<h3>&nbsp;3.2&nbsp;枚举类型配置方法</h3>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019193045891-16599119.png"></p>
<p>&nbsp;生成数据类型示例:</p>
<div class="cnblogs_code">
<pre>export type AddClientToolsGetClientsResponse =<span style="color: rgba(0, 0, 0, 1)"> {
...,
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * 手机识别码
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
phoneCode: string
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * 添加好友状态标识 0-未添加 1-成功添加
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
state: </span>0 | 1
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * 复制次数
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
copyCount: number
}[]</span></pre>
</div>
<h2>四&nbsp;踩坑分享</h2>
<p>4.1&nbsp;yyt.config.ts&nbsp;中的projects--categories--id是个很关键的参数,<strong><span style="color: rgba(128, 0, 0, 1)">如果配置错误,将无法生成ts数据类型定义文件</span></strong>。配置成,是生成配置的token对应的api项目下所有接口的ts数据类型文档,配置成分组id,则生成的是配置的token对应的api项目下,某个分组的api接口请求与响应ts数据类型定义文件。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    projects: [
      {
      categories: [
          {
            id: [</span>139], <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分组id,如果配置成是生成全部</span>
<span style="color: rgba(0, 0, 0, 1)">          },
      ],
      },
    ],</span></pre>
</div>
<p>4.2&nbsp;自动生成api接口请求响应ts数据类型定义文档之后,在业务文件中引用时,却报引用路径有错误。解决方法:&nbsp;以VSCode为例,按Ctrl+Shift+P,然后搜索重启TS服务器,重启TS服务,引用错误就会消失。</p>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019200025917-1143334692.png"></p>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019195532258-379856683.png"></p>
<p>&nbsp;4.3&nbsp; yyt工具对yapi的编写质量有一定的要求,如果后端编写接口太随意,会造成yyt指令执行报错。比如如下的错误,yapi上api的接口地址带有查询参数,而ts的数据类型文件路径是根据api的接口地址生成的,文件路径中是不允许出现?的,一般需要后端修改api地址,如果后端不能及时修改,前端没修改权限又着急用,可以改配置文件</p>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019201520357-764781824.png"></p>
<div class="cnblogs_code">
<pre>    outputFilePath: (interfaceInfo) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      let filePathNames </span>=<span style="color: rgba(0, 0, 0, 1)">interfaceInfo.path;
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ?在路径中是非法的,所以需要截取掉含有?的路径后面的字符 类似这种:getFundManagerAwards?personalCode=101000231.ts</span>
      filePathNames = filePathNames.slice(0, filePathNames.lastIndexOf('?'<span style="color: rgba(0, 0, 0, 1)">));
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> `src/types/httpTypes/${filePathNames}.ts`;
    },</pre>
</div>
<p>4.4&nbsp;yapi&nbsp;get请求的参数,无法指定参数类型,自动生成的参数类型都是string,不能满足实际应用,需要对类型定义不符合使用场景的参数做如下处理:</p>
<p><img src="https://img2020.cnblogs.com/blog/861554/202110/861554-20211019202709285-638603139.png"></p>
<div class="cnblogs_code">
<pre>import type {AddClientToolsGetClientsRequest, AddClientToolsGetClientsResponse} from '@/types/httpTypes/addClientTools/getClients'<span style="color: rgba(0, 0, 0, 1)">;

type TTabVal </span>= 1 | 2<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果要对导出的类型个别字段类型进行修改,参照下方</span>
type TReqParams = Omit&lt;AddClientToolsGetClientsRequest, 'pageNum' | 'pageSize' | 'handleType'&gt; &amp;<span style="color: rgba(0, 0, 0, 1)"> {
pageNum: number;
pageSize: number;
handleType: TTabVal;
};</span></pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/wangpenghui522/p/15426058.html
頁: [1]
查看完整版本: 手摸手教你用 yapi-to-typescript生成Yapi的TypeScript数据类型