浅谈 TypeScript - 泛型在HTTP接口中的运用

1,107 阅读3分钟

不知不觉冬去春来,小明 也换了一份工作,这次他在新的公司里开始负责设计前端项目,一切都从新的开始。有一天 小明 和 公司的后端程序员小李 愉快的对完了接口文档,于是开始埋头苦干。由于请求的异步性,小明 随着知识学习的增长也开始使用 Promise,他对于接口的约定,在实现上也会去考虑复用性,于是 小明小李 完成了一项非常重要的约定:

{
  code: 200,
  message: "",
  data: {}
}

所有的接口都遵从这样的格式。

  • code 代表接口的成功与失败
  • message 代表接口失败之后的服务端消息输出
  • data 代表接口成功之后真正的逻辑

第一期功能的实现一共有三个接口,分别是:

  • 登录
  • 获取列表
  • 退出

小明 使用泛型定义了一个 response 的类型,如:

export enum StateCode {
  error = 400,
  ok = 200,
  timeout = 408,
  serviceError = 500
}

export interface IResponse<T = any> {
  code: StateCode;
  message: string;
  data: T;
}

接着 小明 实现了另外三个接口,这是与后端程序员 小李 一起约定好的格式,如:

export interface ISignOut {
  signOut: boolean;
}

export interface ISignIn{
  Id: string;
  name: string;
  avatar: string;
  permissions: number[];
}

export interface ICommodity {
  id: string;
  img: string;
  name: string;
  price: number;
  unit: string;
}

export interface IFavorites {
  id: string;
  img: string;
  name: string;
  url: string;
}

export interface IList {
  commodity: ICommodity[];
  groups: IFavorites[];
}

由于已经约定好了接口统一使用 POST,Path 也有固定的地址,因此接下来 小明 利用一个开源请求库 axios 编写了一个比较通用的HTTP函数用于项目中:

const ROOT = "https://icepy.me"

export function service({ path, data}: IRequest): Promise<IResponse>{
  return new Promise((resolve) => {
    const request: AxiosRequestConfig = {
      url: `${ROOT}/${path}`,
      method: "POST",
      data: data
    }
    axios(request).then((response: AxiosResponse<IResponse>) => {
      resolve(response.data);
    })
  });
}

小明 休息了会儿,然后大概花了五分钟左右的时间实现了三个接口的业务逻辑:

// 登录
service({
  path: "/signin",
  data: {
    username: "icepy",
    password: "icepy"
  }
}).then((response: IResponse<ISignIn>) => {
  const { code, message, data} = response;
  if (code === StateCode.ok) {
    // 处理 
  }
});

// 退出
service({
  path: "/signout",
  data: {
    id:  "xxx"
  }
}).then((response: IResponse<ISignOut>) => {
  const { code } = response;
  if (code === StateCode.ok) {
    window.location.reload();
  }
})

// 获取列表

service({
  path: "/list",
  data: {
    id: "xxx"
  }
}).then((response: IResponse<IList>) => {
  const { code, data } = response;
  if (code === StateCode.ok) {
    data.commodity.map((v: ICommodity) => {

    });
  }
})

每一个接口的实现,我们都可以从约定的类型中得到 TypeScript 工具的支持,如图:

看了看手表,才 6点半,于是 小明 愉快的下班去请女朋友吃饭了,第二天后端程序员 小李 表示很羡慕,一周之后,小李告诉 小明,由于之前约定的登录接口 name 字段不是很好,现在改成了 nickname,这要是搁以前,小明 肯定懵了,小李笑而不语,(PS:谁叫你下班早),不过不要紧,由于我们使用的是 TypeScript,小明ISignin 接口中将 name 改成了 nickname,于是编辑给了 小明 很明确的反馈,如图:

五分钟之后,小明 愉快的修改完成,看了看手表,6点了,准备好和女朋友共进晚餐。

总结

这就是泛型在HTTP接口实现中一个很帮助的运用,在我们学习完基础的泛型知识后,我们可以将它快速的设计到我们的工作项目中,现代 Web 前端项目,随着业务量的增加,接口也在增加,我们大量的业务逻辑都是依赖于接口而设计的,可以说目前我们用的大部分 Web 应用,都无法避免不使用 HTTP 接口来实现业务逻辑。由于我们使用了 TypeScript 并将接口设计成了一个泛型接口,当随着代码量的增加,我们也会从中获取更多的收益,当然泛型除了用于此处之外,还有很多更有趣的用法。