🚀 在你的项目中,你是如何使用TypeScript进行表单验证的?

📅 发布于 2026年1月 | 👤 作者:博主 | 🏷️ 标签:TypeScript, 表单验证, Zod, ProForm, Ant Design, Web开发, 前端, 面试

欢迎来到我的博客文章!所有文章都是满满的前端干货,文章简明扼要。

✅ 核心方案:Zod + 类型推导 + 表单库集成

1. 用 Zod 定义表单 Schema(同时获得类型和校验逻辑)

// schemas/member.ts
import { z } from 'zod';

export const MemberSchema = z.object({
  key: z.string().min(1, '唯一标识不能为空'),
  workId: z.string().regex(/^\d+$/, '工号必须为数字'),
  name: z.string().min(2, '姓名至少两个字符'),
  department: z.string().min(1, '部门不能为空'),
});

export const FormSchema = z.object({
  members: z.array(MemberSchema).min(1, '至少添加一名成员'),
});

// 自动推导出 TypeScript 类型
export type FormValues = z.infer<typeof FormSchema>;

✅ 优势

避免手动维护 interface,类型与校验规则始终保持一致。

2. 在 ProForm 中使用

ProForm 本身基于 Ant Design Form,支持自定义校验规则。我们将 Zod 规则包装成 AntD 的 Rule:

工具函数:将 Zod 转换为 Ant Design 规则

// utils/zodToAntd.ts
import { Rule } from 'antd/es/form';
import { ZodTypeAny } from 'zod';

export const zodRule = (schema: ZodTypeAny): Rule => ({
  validator(_, value) {
    if (value === undefined || value === null) return Promise.resolve();
    try {
      schema.parse(value);
      return Promise.resolve();
    } catch (err: any) {
      return Promise.reject(new Error(err.errors[0].message));
    }
  },
});

在表单项中使用

// components/MemberForm.tsx
import { ProFormList, ProFormText } from '@ant-design/pro-form';
import { MemberSchema } from '@/schemas/member';
import { zodRule } from '@/utils/zodToAntd';

<ProFormList name="members">
  <ProFormText
    name="workId"
    label="工号"
    rules={[zodRule(MemberSchema.shape.workId)]}
  />
  <ProFormText
    name="name"
    label="姓名"
    rules={[zodRule(MemberSchema.shape.name)]}
  />
</ProFormList>

3. 提交时二次校验(防御性编程)

即使前端有校验,提交时仍用 Zod 做最终检查(防止绕过 UI 直接调用逻辑)

const onFinish = async (values: unknown) => {
  const result = FormSchema.safeParse(values);
  if (!result.success) {
    // 聚焦第一个错误字段或弹出提示
    const firstError = result.error.issues[0];
    message.error(firstError.message);
    return;
  }

  // ✅ 此时 result.data 是类型安全的 FormValues
  await api.submitMembers(result.data.members);
};

完整示例:成员管理表单

1. Schema 定义(schemas/member.ts)

import { z } from 'zod';

export const MemberSchema = z.object({
  key: z.string().min(1, '唯一标识不能为空'),
  workId: z.string().regex(/^\d+$/, '工号必须为数字'),
  name: z.string().min(2, '姓名至少两个字符'),
  department: z.string().min(1, '部门不能为空'),
});

export const FormSchema = z.object({
  members: z.array(MemberSchema).min(1, '至少添加一名成员'),
});

export type Member = z.infer<typeof MemberSchema>;
export type FormValues = z.infer<typeof FormSchema>;

2. 工具函数(utils/zodToAntd.ts)

import { Rule } from 'antd/es/form';
import { ZodTypeAny } from 'zod';

export const zodRule = (schema: ZodTypeAny): Rule => ({
  validator(_, value) {
    if (value === undefined || value === null) {
      return Promise.resolve();
    }
    try {
      schema.parse(value);
      return Promise.resolve();
    } catch (err: any) {
      return Promise.reject(new Error(err.errors[0].message));
    }
  },
});

3. 表单组件(components/MemberForm.tsx)

import { ProForm, ProFormList, ProFormText } from '@ant-design/pro-form';
import { message } from 'antd';
import { MemberSchema, FormSchema, FormValues } from '@/schemas/member';
import { zodRule } from '@/utils/zodToAntd';

export default function MemberForm() {
  const onFinish = async (values: unknown) => {
    const result = FormSchema.safeParse(values);
    if (!result.success) {
      const firstError = result.error.issues[0];
      message.error(firstError.message);
      return;
    }

    // ✅ 类型安全的数据
    const data: FormValues = result.data;
    await api.submitMembers(data.members);
    message.success('提交成功');
  };

  return (
    <ProForm onFinish={onFinish}>
      <ProFormList name="members">
        <ProFormText
          name="workId"
          label="工号"
          rules={[zodRule(MemberSchema.shape.workId)]}
        />
        <ProFormText
          name="name"
          label="姓名"
          rules={[zodRule(MemberSchema.shape.name)]}
        />
        <ProFormText
          name="department"
          label="部门"
          rules={[zodRule(MemberSchema.shape.department)]}
        />
      </ProFormList>
    </ProForm>
  );
}

核心优势总结

✅ 类型安全

✅ 运行时校验

✅ 防御性编程

✅ 开发体验

← 返回首页