是的,Zod 提供了从 TypeScript 类型直接推导并创建 schema 的功能,这样可以避免手动重复定义字段。你可以使用 `z.infer<T>()` 结合 `z.object().shape()` 和 TypeScript 的类型映射来实现这一点。不过需要注意的是,`z.infer<T>()` 通常用于从已有的 Zod schema 中推导类型,而不是直接从 TypeScript 类型推导 Zod schema。但你可以通过一些技巧来实现类似的效果。
为了直接从 `Person` 类型推导 Zod schema,你可以创建一个辅助函数来自动生成 schema。以下是一个示例:
import * as z from 'zod';
interface Person {
age: number;
name: string;
desc?: string;
}
type PersonSchema = z.infer<z.TypeOf<typeof createPersonSchema>>;
const createPersonSchema = () =>
z.object({
age: z.number(),
name: z.string(),
desc: z.string().optional(),
}) as const; // 使用 as const 确保类型被正确推断为只读
// 使用推导的 schema
const personSchema = createPersonSchema();
// 示例验证
const validPerson = personSchema.parse({ age: 30, name: 'John Doe' });
const invalidPerson = personSchema.safeParse({ age: 'not a number', name: 'John Doe' });
console.log(validPerson); // 成功解析的对象
console.log(invalidPerson.success); // false,因为 age 不是数字
虽然上面的代码看起来还是手动定义了 schema,但它被封装在了一个函数中,你可以根据接口的变化只需要修改一个地方。
目前,Zod 并没有直接提供从 TypeScript 接口自动转换到 Zod schema 的内置功能,因为 TypeScript 类型系统和 Zod schema 是两个不同的系统,它们之间有一些不匹配的地方。但是,你可以使用 TypeScript 的工具类型和一些模板字面量类型技巧来减少重复代码。
如果你的接口非常复杂或者有很多这样的接口需要转换,考虑编写一个更通用的工具函数或脚本,根据你的 TypeScript 类型生成相应的 Zod schema 代码。
先定义 schema,然后从 schema 推导出 interface:
这种方式的优点:
避免重复定义
类型和验证规则保持同步
代码更简洁易维护
符合 "单一数据源" 的原则
这样你只需要维护 schema,TypeScript 类型会自动推导出来。