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

React Hook Form:如何复用带不同control与name的useFieldArray组件?

处理React Hook Form v7中嵌套数组字段组件的TypeScript类型问题

我编写了一个处理数组值的Field Array组件,该数组值可在不同表单的不同层级嵌套。向组件传入表单control和对应字段名name以获取值,但在React Hook Form v7版本中无法正确进行类型定义——代码可正常运行,但会触发TypeScript错误。

简化代码示例

import { Control, useFieldArray } from "react-hook-form";

interface Item {
  key: string;
}
interface A {
  items: Item[];
}
interface B {
  data: {
    items: Item[];
  };
}
interface ItemFormFieldsProps<TControl extends Control<A> | Control<B>> {
  control: TControl;
  name: TControl extends Control<A> ? "items" : "data.items";
}

const ItemFormFields = <TControl extends Control<A> | Control<B>>({
  control,
  name,
}: ItemFormFieldsProps<TControl>) => {
  const { fields } = useFieldArray({
    control, // 此处触发TypeScript错误
    name,    // 此处触发TypeScript错误
  });
};

错误信息

control 参数错误

Type 'Control<A, any> | Control<B, any>' is not assignable to type 'Control<A, any> | undefined'.
  Type 'Control<B, any>' is not assignable to type 'Control<A, any>'.
    The types of '_subjects.state' are incompatible between these types.
      Type 'FormStateSubjectRef<B>' is not assignable to type 'FormStateSubjectRef<A>'.
        Type 'FormStateSubjectRef<B>' is not assignable to type '{ readonly observers: Observer<Partial<FormState<A>> & { name?: string | undefined; }>[]; subscribe: (value: Observer<Partial<FormState<A>> & { ...; }>) => Subscription; unsubscribe: Noop; }'.
          Types of property 'observers' are incompatible.
            Type 'Observer<Partial<FormState<B>> & { name?: string | undefined; }>[]' is not assignable to type 'Observer<Partial<FormState<A>> & { name?: string | undefined; }>[]'.
              Type 'Observer<Partial<FormState<B>> & { name?: string | undefined; }>' is not assignable to type 'Observer<Partial<FormState<A>> & { name?: string | undefined; }>'.
                Type 'Partial<FormState<A>> & { name?: string | undefined; }' is not assignable to type 'Partial<FormState<B>> & { name?: string | undefined; }'.
                  Type 'Partial<FormState<A>> & { name?: string | undefined; }' is not assignable to type 'Partial<FormState<B>>'.
                    Types of property 'defaultValues' are incompatible.
                      Type 'A | Readonly<{ items?: ({ key?: string | undefined; } | undefined)[] | undefined; }> | undefined' is not assignable to type 'B | Readonly<{ data?: { items?: ({ key?: string | undefined; } | undefined)[] | undefined; } | undefined; }> | undefined'.
                        Type 'A' is not assignable to type 'B | Readonly<{ data?: { items?: ({ key?: string | undefined; } | undefined)[] | undefined; } | undefined; }> | undefined'.ts(2322)
fieldArray.d.ts(8, 5): The expected type comes from property 'control' which is declared here on type 'UseFieldArrayProps<A, "items", "id">'

name 参数错误

Type '"items" | "data.items"' is not assignable to type '"items"'.
  Type '"data.items"' is not assignable to type '"items"'.ts(2322)
fieldArray.d.ts(6, 5): The expected type comes from property 'name' which is declared here on type 'UseFieldArrayProps<A, "items", "id">'

解决方案

问题根源在于泛型约束直接使用Control<A> | Control<B>,TypeScript无法在useFieldArray中正确推断联合类型的分支,导致类型不匹配。以下是两种可行的修复方案:

方案1:针对指定表单类型的精确约束

将表单根类型作为泛型参数,通过条件类型关联control与name的类型:

import { Control, useFieldArray, useForm } from "react-hook-form";

interface Item {
  key: string;
}
interface A {
  items: Item[];
}
interface B {
  data: {
    items: Item[];
  };
}

// 以表单根类型作为泛型参数
interface ItemFormFieldsProps<T extends A | B> {
  control: Control<T>;
  name: T extends A ? "items" : "data.items";
}

const ItemFormFields = <T extends A | B>({
  control,
  name,
}: ItemFormFieldsProps<T>) => {
  // 通过泛型分支让TypeScript正确推断参数类型
  const { fields } = useFieldArray({
    control,
    name,
  } as T extends A 
    ? { control: Control<A>, name: "items" } 
    : { control: Control<B>, name: "data.items" });
  
  // 此处返回组件内容
  return null;
};

// 正确使用示例
const UsageA = () => {
  const { control } = useForm<A>();
  return <ItemFormFields control={control} name="items" />;
};

const UsageB = () => {
  const { control } = useForm<B>();
  return <ItemFormFields control={control} name="data.items" />;
};

方案2:通用型约束(支持任意含Item数组的表单)

通过工具类型自动匹配表单中所有指向Item[]的字段路径:

import { Control, useFieldArray, Path, FieldValues, useForm } from "react-hook-form";

interface Item {
  key: string;
}
interface A {
  items: Item[];
}
interface B {
  data: {
    items: Item[];
  };
}

// 自动提取表单中所有指向Item[]的字段路径
type ItemArrayPath<T extends FieldValues> = {
  [K in Path<T>]: T[K] extends Item[] ? K : never;
}[Path<T>];

interface ItemFormFieldsProps<T extends FieldValues> {
  control: Control<T>;
  name: ItemArrayPath<T>;
}

const ItemFormFields = <T extends FieldValues>({
  control,
  name,
}: ItemFormFieldsProps<T>) => {
  const { fields } = useFieldArray({ control, name });
  return null;
};

内容的提问来源于stack exchange,提问作者adesurirey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 13:30:47