0Pricing
React Academy · 课时

类型安全的表单与 API 响应契约

使用 Zod 从架构推断 TypeScript 类型,并验证表单数据和 API 响应。

类型安全的表单与 API 响应契约 是 CoddyKit 上的免费 React Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。

类型安全的数据结构为何重要

表单和 API 响应是数据从不可信来源进入应用的边界。Zod 让您可以定义既能在运行时验证、又能推断 TypeScript 类型的模式。

Zod 模式基础

使用 Zod 的链式 API 定义模式。使用 z.infer<typeof schema> 提取 TypeScript 类型。

import { z } from 'zod';

const UserSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().min(0).max(150).optional(),
  role: z.enum(['admin', 'user', 'guest']),
});

type User = z.infer<typeof UserSchema>;
// { id: string; name: string; email: string; age?: number; role: 'admin'|'user'|'guest' }

使用 Zod 解析器的 React Hook Form

将 Zod 模式与 React Hook Form 集成,以实现类型安全的表单验证——TypeScript 知道表单数据和错误的结构。

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';

const LoginSchema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'At least 8 characters'),
});
type LoginData = z.infer<typeof LoginSchema>;

function LoginForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<LoginData>({
    resolver: zodResolver(LoginSchema),
  });

  const onSubmit = (data: LoginData) => {
    // data is LoginData — fully typed, already validated
  };
}

验证 API 响应

使用 Zod 解析 API 响应,在边界处捕获结构不匹配问题——如果 API 返回了意外数据,您会得到详细错误,而不是等到组件深处发生运行时崩溃。

async function fetchUser(id: string): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  const json = await res.json();
  return UserSchema.parse(json); // throws ZodError if shape is wrong
}

使用安全解析优雅地处理错误

使用 schema.safeParse() 获取结果对象,而不是抛出错误——这非常适合需要向用户显示错误的表单验证场景。

const result = UserSchema.safeParse(formData);
if (!result.success) {
  const errors = result.error.flatten().fieldErrors;
  // { name: ['Must be at least 1 character'], email: ['Invalid email'] }
  return errors;
}
const user = result.data; // User — fully typed

使用 Zod 的判别联合

使用 z.discriminatedUnion 对 API 响应进行建模,使其根据成功或错误标志具有不同的结构。

const ApiResponse = z.discriminatedUnion('ok', [
  z.object({ ok: z.literal(true), data: UserSchema }),
  z.object({ ok: z.literal(false), error: z.string(), code: z.number() }),
]);

type ApiResult = z.infer<typeof ApiResponse>;
// { ok: true; data: User } | { ok: false; error: string; code: number }

Zod 转换

使用 .transform() 在解析期间强制转换或重新组织数据,例如将日期字符串转换为 Date 对象。

const DateSchema = z.string().transform(s => new Date(s));
// Input: '2024-01-15' → Output: Date object

const EventSchema = z.object({
  title: z.string(),
  date: z.string().pipe(z.coerce.date()),
});
type Event = z.infer<typeof EventSchema>;
// { title: string; date: Date }

复用模式

从基础模式派生创建和更新模式,以避免重复。

const UserSchema = z.object({ name: z.string(), email: z.string().email() });

// For creation: add password
const CreateUserSchema = UserSchema.extend({ password: z.string().min(8) });

// For update: all fields optional
const UpdateUserSchema = UserSchema.partial();

type CreateUser = z.infer<typeof CreateUserSchema>;
type UpdateUser = z.infer<typeof UpdateUserSchema>;

使用 Zod 的服务器操作

在访问数据库之前,使用 Zod 在 Next.js 服务器操作中验证表单数据。

async function createPost(formData: FormData) {
  'use server';
  const schema = z.object({ title: z.string().min(3), body: z.string().min(10) });
  const result = schema.safeParse(Object.fromEntries(formData));
  if (!result.success) return { errors: result.error.flatten().fieldErrors };
  await db.post.create({ data: result.data });
  revalidatePath('/blog');
  redirect('/blog');
}

前端与后端之间共享模式

从共享包中导出 Zod 模式,让前端和后端使用相同的验证逻辑,使一个模式成为唯一可信来源。

// packages/schemas/src/user.ts
export const CreateUserSchema = z.object({ ... });
export type CreateUser = z.infer<typeof CreateUserSchema>;

// Frontend imports:
import { CreateUserSchema, CreateUser } from '@company/schemas';
// Backend imports:
import { CreateUserSchema } from '@company/schemas';

Zod 错误消息

为每个字段自定义错误消息,以改善用户体验;Zod 会通过解析器自动将这些消息传递给表单的错误显示组件。

const RegistrationSchema = z.object({
  username: z.string()
    .min(3, 'Username must be at least 3 characters')
    .max(20, 'Username cannot exceed 20 characters')
    .regex(/^[a-z0-9_]+$/, 'Only lowercase letters, numbers, and underscores'),
});

快速检查

z.infer<typeof MySchema> 在 TypeScript 中提供什么?

回顾

只定义一次 Zod 模式,并使用 z.infer 派生 TypeScript 类型。在 React Hook Form 中使用 zodResolver 进行类型安全的验证。在边界处使用 Zod 解析 API 响应,使用 safeParse 处理面向用户的错误,并通过共享包在前端和后端之间共享模式。

常见问题解答

「类型安全的表单与 API 响应契约」课时是免费的吗?

是的 — 「类型安全的表单与 API 响应契约」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。

「类型安全的表单与 API 响应契约」这节课中我会学到什么?

使用 Zod 从架构推断 TypeScript 类型,并验证表单数据和 API 响应。 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 React Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「类型安全的表单与 API 响应契约」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 React Academy 课中编写并运行代码吗?

能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 组件变体的可辨识联合类型
  2. React 中的条件类型与映射类型
  3. 带有 'as' 属性的多态组件
  4. 类型安全的表单与 API 响应契约
← 返回 React Academy