如何从 TypeScript 接口自动生成 Zod 校验 Schema

本文详解如何将 typescript 接口(interface)安全、类型精准地映射为 zod schema,解决泛型约束错误与类型不匹配问题,并提供可复用的最佳实践方案。

在使用 Zod 进行运行时类型校验时,开发者常希望其 Schema 能严格对齐已有 TypeScript 接口(如 Test1),以实现编译期类型与运行时校验的一致性。但直接将接口作为泛型参数传入 z.object() 会报错——这是因为 z.object() 的泛型要求必须是 ZodRawShape 类型(即键值对中值为 Zod Schema 实例的对象),而非普通 TS 接口。

正确做法是分离类型声明与 Schema 构建:先定义接口描述数据结构,再手动创建 Zod Schema,并通过类型断言或显式类型标注确保二者一致。例如:

import { z } from 'zod';

export interface Test1 {
  device_uuid: string;
  serial: string;
}

// ✅ 正确:显式标注返回类型为 z.ZodType
const schema: z.ZodType = z.object({
  device_uuid: z.string().uuid(),
  serial: z.string(), // 必须与 interface 中的 serial: string 保持一致
});

⚠️ 注意事项:

  • 不要尝试 z.object(...) —— Test1 是普通接口,不满足 ZodRawShape 约束(缺少索引签名且值非 Zod Schema);
  • 若 Schema 字段类型与接口不一致(如将 serial 写成 z.number()),TS 会立即报错:Type 'number' is not assignable to type 'string',这正是我们依赖的类型保护机制;
  • 如需复用校验逻辑,可进一步导出 schema 的 inferred 类型:
    export type Test1Input = z.infer; // 等价于 Test1

✅ 进阶技巧:配合 satisfies(TS 4.9+)提升可读性与安全性:

const schema = z.object({
  device_uuid: z.string().uuid(),
  serial: z.string(),
}).satisfies>();

该写法既避免冗余类型标注,又强制校验 Schema 结构是否完全兼容 Test1。

总结:Zod 并不支持“自动推导接口为 Schema”,但通过显式类型标注 z.ZodType 或 satisfies 断言,可实现零成本、强一致的接口-Schema 同步,兼顾开发体验与类型安全。