You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 02:57:02