🚀 如何确保TypeScript代码与后端API的兼容性
欢迎来到我的博客文章!所有文章都是满满的前端干货,文章简明扼要。
✅ 一、核心原则:前后端共享类型定义(Shared Types)
目标
让前端和后端使用同一套接口类型,避免"口头约定"导致的字段不一致。
方案 1:从后端生成 TypeScript 类型(推荐)
这是最可靠的方式,尤其适合你使用 Node.js、NestJS、Spring Boot 等支持 OpenAPI/Swagger 的后端。
步骤:
- 后端提供 OpenAPI (Swagger) 文档(例如 /api-docs.json)
- 前端用工具自动生成 TS 类型和 API 客户端
npx openapi-typescript https://api.example.com/api-docs.json -o src/types/api.ts
生成的类型示例:
export interface User {
id: number;
name: string;
email: string;
createdAt: string;
}
export type GetUsersResponse = User[];
✅ 二、运行时校验:防止"类型正确但数据异常"
即使 TS 类型匹配,网络传输或后端 bug 仍可能导致数据不符合预期。因此需运行时校验。
使用 Zod(推荐)或 Yup 进行数据验证
完整示例项目
1. 安装依赖
npm install zod
npm install -D openapi-typescript @types/node
2. 配置 package.json 脚本
{
"scripts": {
"gen:types": "node scripts/generate-api-types.ts",
"dev": "vite",
"build": "npm run gen:types && vite build"
}
}
3. 编写类型生成脚本(scripts/generate-api-types.ts)
import { generate } from 'openapi-typescript';
import { writeFileSync } from 'fs';
const OPENAPI_URL = 'https://your-backend.com/api/openapi.json';
async function main() {
console.log('正在生成 API 类型...');
const schema = await generate(OPENAPI_URL, {
exportType: true,
pathParamsAsTypes: true,
});
writeFileSync('./src/api/types/index.ts', schema);
console.log('✅ API 类型已生成到 src/api/types/index.ts');
}
main().catch(console.error);
4. 自动生成的类型示例(src/api/types/index.ts)
运行 npm run gen:types 后,会生成类似:
export interface User {
id: number;
name: string;
email: string;
createdAt: string;
}
export interface GetUserResponse {
data: User;
}
export type paths = {
'/api/users/{id}': {
get: {
parameters: { path: { id: number } };
responses: { 200: { content: { 'application/json': GetUserResponse } } };
};
};
};
5. 编写 Zod Schema(src/api/schemas/user.schema.ts)
import { z } from 'zod';
export const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
createdAt: z.string().datetime(),
});
export type User = z.infer<typeof UserSchema>;
6. 封装 API 客户端(src/api/client.ts)
import { UserSchema } from './schemas/user.schema';
import type { User } from './schemas/user.schema';
async function request<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error('HTTP ' + res.status);
const json = await res.json();
return json as T;
}
export async function fetchUser(id: number): Promise<User> {
const data = await request('/api/users/' + id);
return UserSchema.parse(data);
}
🧩 7. 业务服务层(src/services/user.service.ts)
import { fetchUser } from '@/api/client';
export class UserService {
static async getUserProfile(userId: number) {
try {
const user = await fetchUser(userId);
return user;
} catch (error) {
console.error('获取用户失败:', error);
throw error;
}
}
}
8. 使用示例(React 组件)
import { useState, useEffect } from 'react';
import { UserService } from '@/services/user.service';
export default function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
UserService.getUserProfile(userId)
.then(setUser)
.catch(err => {
alert('加载用户失败');
});
}, [userId]);
if (!user) return <div>加载中...</div>;
return <div>欢迎, {user.name}!</div>;
}
npm run gen:types 干啥用的?
🔄 典型工作流
- 后端更新了 API(比如新增了一个 phone 字段)
- 前端开发者运行
npm run gen:types - 自动生成的新类型包含 phone 字段
- TypeScript 编译器会提示所有需要更新的地方
- 前端开发者根据提示更新代码
✅ 核心优势
- 类型安全:编译时检查,避免字段拼写错误
- 运行时校验:Zod 确保数据格式正确
- 自动同步:后端 API 变更自动反映到前端类型
- 开发体验:IDE 自动补全和类型提示