围观前后端对接的 TypeScript 最佳实践,我们缺什么?

不知道大家有没有跟我一样的情况,就是 Typescript 用了那么久,总是不得其法。

  • 想写好,就需要耗费大量的时间精力,跟着后端 api 文档一个个对着写。
  • 想偷懒,写几个any,代码出了bug,又不好处理。

而且不只普通通开发者有这种苦恼,就连大神也无法避免。之前就有报道大神放弃了在项目中使用 Typescript。

但是用 AI 编码之后,为了避免 AI 出现低级错误,我还是毅然选择了 Typescipt。调试中能看到 AI 写的错误,而且最新的大模型还能根据这些报错自行修正。用起来的确方便了不少。

既然 Typescript 还是必须,那何不让 AI 给出最佳实践,帮我们突破一下之前使用 Typescript 的局限?

说干就干,三两句话,AI 就列出了目前可用的几种方案。

一、方案总览

目前来说,有四种方案。AI 贴心地给出了不同项目场景下的方案选择方法。

场景 推荐方案
传统 REST API OpenAPI + 自动生成
GraphQL 架构 GraphQL Codegen
灵活接口/无文档 Zod 运行时校验
全栈 TypeScript tRPC(替代方案)

全栈的 tPRC 方案,可以去官网围观。Node.js tRPC官网

下面是 AI 给出的 1-3 种方案的具体代码。

二、具体实践方案

方案 1:使用 OpenAPI 规范(推荐)

适用场景:RESTful API,后端支持 Swagger/OpenAPI 规范

bash 复制代码
# 安装代码生成工具
npm install openapi-typescript @openapitools/openapi-generator-cli -D
  1. 生成 TypeScript 类型

    bash 复制代码
    npx openapi-typescript http://后端地址/swagger.json -o src/api/types.d.ts
  2. 生成 API Client

    bash 复制代码
    npx @openapitools/openapi-generator-cli generate \
      -i http://后端地址/swagger.json \
      -g typescript-axios \
      -o src/api/client
  3. 前端调用示例

    typescript 复制代码
    import { UserApi, UserDto } from './api/client';
    
    const userApi = new UserApi();
    
    // 完全类型安全的调用
    const getUser = async (id: number): Promise<UserDto> => {
      const res = await userApi.getUserById(id);
      return res.data; // 自动推断为 UserDto 类型
    };

这个方案比较好理解,就是使用工具,读取 swagger.json,直接输出一份 type 文件,还有请求的函数集。

优点很明显,全自动化同步,后端修改能自动更新前端类型。

而缺点就是有个前提,后端必须按规范好好地写代码,才能输出正确的 swagger。另外这个方案会输出封装好的 axios,理解有一点费劲,修改有一定的麻烦。

方案 2:GraphQL + Codegen

适用场景:使用 GraphQL 的现代架构

bash 复制代码
# 安装依赖
npm install @apollo/client graphql
npm install @graphql-codegen/cli -D
  1. 配置 codegen.yml

    yaml 复制代码
    schema: http://后端地址/graphql
    documents: src/**/*.graphql
    generates:
      src/generated/graphql.ts:
        plugins:
          - typescript
          - typescript-operations
          - typescript-react-apollo
  2. 定义查询

    graphql 复制代码
    # src/users/user.query.graphql
    query GetUser($id: ID!) {
      user(id: $id) {
        id
        name
        email
      }
    }
  3. 生成类型

    bash 复制代码
    npx graphql-codegen
  4. 前端调用

    typescript 复制代码
    import { useGetUserQuery } from './generated/graphql';
    
    function UserProfile({ id }: { id: string }) {
      const { data } = useGetUserQuery({ variables: { id } });
      // data.user 自动具备完整类型提示
    }

同样的,基于 GraphQL 框架,也有代码生成器,只需要配置好相关文档,也能输出 type 文件来。

GraphQL 框架,一定程度上减少了增删改查接口开发的工作量,把数据库、前后端工作串联起来了。所以这个方案是优于方案 1,但她仅针对于使用了 GraphQL 框架的项目。

方案 3:手动定义 + Zod 运行时校验

适用场景:GraphQL、未提供 OpenAPI 的 REST API

bash 复制代码
# 安装校验库
npm install zod
  1. 定义共享类型

    typescript 复制代码
    // src/api/schemas.ts
    import { z } from 'zod';
    
    // 1. 定义后端接口响应基础结构
    export const ApiResponseSchema = z.object({
      code: z.number(),
      data: z.any().optional(),
      message: z.string().optional(),
    });
    
    // 2. 定义具体业务 DTO
    export const UserSchema = z.object({
      id: z.number(),
      name: z.string(),
      email: z.string().email(),
    });
    
    export type User = z.infer<typeof UserSchema>;
  2. 封装 API 请求器

    typescript 复制代码
    // src/api/client.ts
    import axios, { AxiosResponse } from 'axios';
    import { ApiResponseSchema, UserSchema } from './schemas';
    
    // 增强版 axios 实例
    const apiClient = axios.create({
      baseURL: '/api',
      timeout: 10000,
    });
    
    // 响应数据校验拦截器
    apiClient.interceptors.response.use((response) => {
      const validated = ApiResponseSchema.parse(response.data);
      
      // 具体业务数据校验
      if (validated.data) {
        // 根据接口路径动态校验(实际项目需完善路由匹配逻辑)
        if (response.config.url?.includes('/users/')) {
          validated.data = UserSchema.parse(validated.data);
        }
      }
      
      return { ...response, data: validated };
    });
  3. 前端调用

    typescript 复制代码
    // 获取用户
    const fetchUser = async (id: number): Promise<User> => {
      const res = await apiClient.get(`/users/${id}`);
      return res.data.data; // 自动推断为 User 类型
    };

这个方法应该是适合大部分项目的方案了,它的优点是,运行时校验兜底,防止类型欺骗。

Zod 库有一个好处就是省去了不少写验证方法的时间。

但是缺点就再明显不过了,前端还是要写大量的代码来实现。

总结

理论上来看,无论是传统 REST API,还是新的 GraphQL 架构其实都有工具自动生成 type 文件,前端基本上调用生成的即可。

但是实践起来往往就不是那么一回事了。

  1. 传统 REST API 中,估计很多项目都是老旧项目了。swagger支持一般。如果要重新写 swagger,无疑是增加了后端的工作量。
  2. GraphQL 架构,目前我经历过的,见过的几乎没有任何一家国内公司用的。
  3. 对于方案四的 tRPC,需要前后端都用 Node 实现,现在国内也很少有这样的公司。

综上来看,执行起来还是困难重重。

唯独前端仔手动方案写类型的方案,才是最实际的。如果你还有能力改,可以尝试用 Zod 减少一点工作量吧。

启发

这件事也给我一些启发:

  1. 一件事总需要一个人干,如果不是你干就是别人干,别人不干就是你干,工作量不会凭空消失。
  2. 标准化的流程越早执行越好。排除一切困难执行,最后带来的收益一定是值得的。

第二点也是我一直想的一个事情,对标准化的严格执行,其实就是我们缺失的。

我们前端还在争论要不要配置 eslint,后端还在争论要不要执行 Restful API 的时候,别人可能已经把整个流程融合起来,开发了配套的生态内容。而不去争论,直接用一套标准解决方案来做的时候,往往才是最省时省力的。

于是,在国外转向 AI 的时候,由于一切都是标准化的,整个运转起来也飞快。而我们,虽然也在发展,但总感觉一直在追随它人的脚步。

这种情况,甚至还延伸到企业信息化,总有企业说有自己的流程,于是搞出了很多奇奇怪怪的项目、子项目。在企业发展过程中,越来越繁杂,越来越冗余,最后变革起来就越来难。

对此,你有什么看法?

欢迎在评论区留下你的足印。

相关推荐
kaliarch3 分钟前
IaC 管控资源发生属性偏移修正方案
后端·架构·自动化运维
kaliarch10 分钟前
Terraform 合并多个项目(独立目录)解决方案
后端·自动化运维
南囝coding13 分钟前
最近Vibe Coding的经验总结
前端·后端·程序员
丘山子27 分钟前
Python 布尔运算的优雅实践
后端·python·面试
前端小咸鱼一条39 分钟前
React组件化的封装
前端·javascript·react.js
汪子熙41 分钟前
理解 SSH Agent 的工作原理与应用场景
后端
随便起的名字也被占用1 小时前
leaflet中绘制轨迹线的大量轨迹点,解决大量 marker 绑定 tooltip 同时显示导致的性能问题
前端·javascript·vue.js·leaflet
苏琢玉1 小时前
如何优雅地处理多种电商优惠规则?我用 PHP 封装了一个 Promotion Engine
后端·php·composer
豌豆花下猫1 小时前
Python 潮流周刊#113:用虚拟线程取代 async/await
后端·python·ai
南方kenny1 小时前
TypeScript + React:让前端开发更可靠的黄金组合
前端·react.js·typescript