ts-api-loader

an awesome ts-api / rc d.ts translator

Usage no npm install needed!

<script type="module">
  import tsApiLoader from 'https://cdn.skypack.dev/ts-api-loader';
</script>

README

一个接口对接方案

快速了解demo

http://gitlab.alibaba-inc.com/javaer.ljw/api-loader-demo

motivation

  • 简化/结构化 web 项目 api 请求代码, 提高团队协作开发和阅读效率, 并为可视化提供可能性
  • 运行时自动根据 typescript 类型 mock 数据 (可选是否使用)
  • 运行时自动根据 typescript 类型 check 数据(可选是否使用)
  • low-code practice
  • 强迫业务开发人员多写注释, 少给别人挖坑

设计文档

https://yuque.antfin-inc.com/recore/api-loader/introduce

例子🌰

import { TIndexCards, TVideoCards, TDemandData, EmbedStruct } from './demo.type';
import * as ajax from '@ali/api-runtime/lib/request/ajax';

const host = process.env.isDev ? 'https://mocks.alibaba-inc.com' : '';

const willFetch = (axiosParam: AxiosRequestConfig) => {
  // modify axiosParam. eg axiosParam.header={csrf_token: window.csrf_token}
  return axiosParam;
}

/**
 * 用电量接口请求组, class 上的注释项/注解项会被method复用
 * class 上的注解会被方法继承使用, 即所有方法都会执行该 willFetch
 * 更多 ajax 用法参考 https://web.npm.alibaba-inc.com/package/@ali/api-runtime
 * @kind ajax
 * @host https://mocks.alibaba-inc.com
 * @baseUrl /mock/limitless/demo
 * @method get
 */
@ajax.willFetch(willFetch) // api-loader^0.7.0开始支持注解, 依赖最新 vscode-recore 插件支持该语法
declare class DemoApi {
  /**
   * 查询指标看板数据
   * @url /indexcards.json
   */
  getIndexCards(foo: string, bar: number): Promise<TIndexCards>;

  /**
   * `@mock注释` 表示该请求自动根据返回的 ts 类型 mock, 不发起真实请求
   * @mock true
   * @url /videocards.json
   */
  @ajax.host(host) // 覆盖 class 注释的 host, 且支持变量
  @ajax.willFetch((axiosParam) => {...}) // 覆盖 class 注释的 willFetch
  getVideoCards(): Promise<TVideoCards>;
}

基本用法

安装

recore 项目内置,不需要安装步骤

  1. tnpm i --save @ali/api-loader
  2. 在webpack中添加如下loader, 注意该loader要在 ts-loader 执行之前
{
  'test': /(\.api|\.type)\.ts$/, // *.api.ts 和 *.type.ts 需要过 api-loader
  'use': [
    {
      loader: '@ali/api-loader',
      options: {
        keepComment: false // 是否保留注释
        log: false // 是否显示 loader 执行日志
        mock: env === 'development' // webpack开发模式下才启用mock
      }
    }
  ]
}
  1. 按照上述例子编写 x.api.ts 文件. (类型声明较多时建议将声明独立到 x.type.ts 文件, 方便业务引用)

进阶使用

上述方式应该基本满足日常 web 项目开发需要, 但如果遇到需要自定义 kind, 例如不用 http request, 想用 fetch 实现, 或者有自己包装的其他协议(如 mqtt/zeromq 等), 则可以通过本节介绍的拓展 kind 方法

自定义 kind

如果不希望使用 @ajax, 或者想自己不基于 axios 自己写一套请求,可以使用如下方式注册一个 kind:

在工程目录下配置 api.config.js 文件,内容如下

module.exports = {
  // 此处注册的 myAjax 后续可以在 *.api.ts 中使用 @kind myAjax
  myAjax: './src/request/myAjax',
  // 此处注册的 myRequestKind2 后续可以在 *.api.ts 中使用 @kind myRequestKind2
  myRequestKind2:  [ // 如果请求有多个步骤(拆分到几个模块的), 可以用数组格式 {name, path}[]
    { name: 'step1', path: './src/mystep1' },
    { name: 'ajax', path: './src/request/myAjax' },
    { name: 'adaptor', path: './src/request/myAdaptor' },
    ...
  ]

自定义 kind 发布到 npm 包

如果团队有沉淀了一套稳定成熟的 kind, 希望在多个项目中以 npm 的形式复用, 而不是每次都要拷贝上述 api.config.js 文件, 可以使用如下方式发包.

npm 包的 index.js (或package.json指定的main) 所暴露的是如下格式

module.exports = {
  myAjax: '[npm_package_name]/lib/myAdaptor',
  myRequestKind2:  [
    { name: 'step1', path: '[npm_package_name]/lib/mystep1' },
    { name: 'ajax', path: '[npm_package_name]/lib/myRequest' },
    { name: 'adaptor', path: '[npm_package_name]/lib/myAdaptor' },
    ...
  ]
}

在业务项目工程下, 安装该 npm 包, 然后在业务项目工程的 package.json 文件中加上这个配置项

"@api-loader-kinds": "[npm_package_name]"
// 或多个 npm 包组合
"@api-loader-kinds": [
  "[npm_package1_name]",
  "[npm_package2_name]",
  ...
]

注意, 如果上述 kind 有重名, 则后配置的 kind 会覆盖原有配置的 kind, 即覆盖顺序为 @ali/api-runtime < package.json("@api-loader-kinds") < api.config.js

Q&A

  1. Q: A decorator can only decorate a method implementation, not an overload.怎么办 A: 安装最新的 Vscode Recore插件

  2. Q: Unable to resolve signature of class decorator when called as an expression. A: 同上解决办法

  3. Q: 自定义 kind 如何定义注解项 A: 参考 @ali/api-runtime/request/ajax

  4. Q: 我没有用到的 kind 会占用打包体积吗 A: 不会, 是按需打包的

  5. Q: 编译的数据结构为什么不用 json-schema A: 实际上已经是类 json-schema 了, 但由于 ts 有一些诸如 extends, &, 嵌套引用等用 json-schema 不好描述, 所以折中拓展了一下 json-schema

  6. Q: 所有的 typescript 类型声明系统都支持吗 A: 并不. typescript 语法太多了, 有些其实反而带来阅读成本, 例如 omit 之类的. 我们设计 apits 的初衷是为了让人(协作)和机器(可视化)都能高效. 太复杂也难做静态语法分析和转义到 js 代码. 因此最复杂的能支持到如下类型声明

interface A {
  a: Array<string>;
  b: 'man' | 'woman' | 1 | true;
  c: boolean | string;
  d: number;
  e: 1;
  f: {
    sub: string
  }
}

interface B {
  a: A;
  b: string[];
}

interface C {
  c: A & B & {x: string}
}

type D = A & B & {y: string};

interface E extends B, D {
  e: number
}

即包含了 基础类型, extends, &, 嵌套, Array. 泛型目前只支持 Array. 不过 ts 官方建议写 xx[]. 所以等于是不支持泛型

changelog

0.7.9 支持 //@api-ignore 忽略段落

changelog

0.7.8 🐞fix windows 机器CRLF 0.7.7 🐞当返回类型漏写 Promise 时也能兼容 0.7.6 🐞复杂相互嵌套的场景下, 如 interface A {child: B}; interface B {child: A} 的支持 0.7.4 🐞type 声明自嵌套 fix 🐞npm 包的 type 报 typedeclare cant not use as variable 问题 0.7.1 ✨支持 windows 0.7.0 ✨支持 ts 类型组合/类型继承 ✨增加注解支持