TypeScript如何转换递归泛型以适配Zod表单的字段类型结构
实现方案
核心是通过条件类型+递归类型实现对输入类型的深度转换,你需要先定义一个通用的递归转换工具类型,再替换原有代码里的单层字段类型定义即可。
1. 定义递归转换工具类型
import { z } from "zod"; // 递归转换类型:将任意输入类型转成表单字段结构 type FormField<T> = // 处理数组:对数组每一项递归应用转换 T extends Array<infer ItemType> ? FormField<ItemType>[] // 处理对象:对对象每个属性递归应用转换 : T extends Record<string, any> ? { [K in keyof T]: FormField<T[K]> } // 处理基础类型:包装成带value/onChange/errors的字段结构 : { value: T; onChange: (newValue: T) => void; errors?: string[]; };
2. 替换原有类型定义
把你原有代码里的Fields类型替换为上面的工具类型即可:
const example = <Input extends Record<string, any>, Output>( schema: z.ZodType<Output, any, Input> ) => { // 用递归工具类型生成最终的表单结构类型 type Fields = FormField<Input>; return ({} as unknown) as Fields; }; // 测试效果 const schema = z .object({ name: z.string().min(3, 'Too short.'), nested: z.object({ name: z.string(), }), repeat: z.array(z.object({ arrNest: z.string(), })), }).transform((v) => v.name); export const typed = example(schema);
效果验证
此时typed的类型会完全符合你的预期:
typed.name类型为{ value: string; onChange: (newValue: string) => void; errors?: string[] }typed.nested.name类型和上面一致typed.repeat类型为{ arrNest: { value: string; onChange: (newValue: string) => void; errors?: string[] } }[]
扩展建议
如果后续需要支持运行时自动生成对应结构的表单实例,只需要在运行时递归遍历Zod Schema节点:
- 判断是
ZodObject就遍历其shape属性的所有字段 - 判断是
ZodArray就处理其element属性的子Schema - 基础类型生成对应的
value/onChange/errors结构即可,和上面的类型完全对齐。
内容的提问来源于stack exchange,提问作者exrhizo
相关产品推荐
相关产品推荐

