我正在尝试实现可在根表单级别和嵌套字段中使用的表单字段组件。
export type FirstNameField = {
firstName: string;
};
type GenericFormType<T, NS extends string | never = never> = NS extends never ? T : Record<NS, T>;
export type FirstNameProps<T extends string | never = never> = {
namespace?: T;
};
export const FirstName = <T extends string | never = never>({ namespace }: FirstNameProps<T>) => {
const form = useFormContext<GenericFormType<FirstNameField, T>>();
return (
<FormInput<GenericFormType<FirstNameField, T>>
{...form}
fieldName={namespace ? `${namespace}.firstName` : 'firstName'}
label="First name"
validation={{
required: 'Required',
}}
/>
);
};
interface Props<T extends FieldValues = FieldValues> extends UseFormReturn<T> {
label?: ReactNode;
fieldName: FieldPath<T>;
validation?: RegisterOptions;
}
export function FormInput<T extends FieldValues = FieldValues>(props: Props<T>) {
return null;
}
所以有错误 在行
fieldName={namespace ? `${namespace}.firstName` : 'firstName'}
说的是
TS2322: Type 'firstName' | `${T}.firstName` is not assignable to type Path<GenericFormType<FirstNameField, T>>
Type string is not assignable to type Path<GenericFormType<FirstNameField, T>>
所以我尝试使用泛型类型和其他类型,但没有成功
当我努力解决这个问题时,我通过将名称设置为与 T 类似的类型来解决它,但扩展了路径(路径来自react-hook-form)。在你的例子中就是这样。
interface Props<T extends FieldValues = FieldValues, TName extends Path<TFieldValues> = Path<TFieldValues>,> extends UseFormReturn<T> {
label?: ReactNode;
fieldName: TName;
validation?: RegisterOptions;
}
然后
export function FormInput<T extends FieldValues = FieldValues,TName extends Path<TFieldValues> = Path<TFieldValues>>(props: Props<T, TName>) {
return null;
}
我的自定义表单组件如下所示,它将其他输入组件(输入、文本区域、复选框、选择)作为子组件:
interface FormComponentProps<
TFieldValues extends FieldValues = FieldValues,
TName extends Path<TFieldValues> = Path<TFieldValues>,
> extends HTMLAttributes<HTMLDivElement> {
methods: UseFormReturn<TFieldValues>
name: TName
children: ReactNode
}
export function FormComponent<
TFieldValues extends FieldValues = FieldValues,
TName extends Path<TFieldValues> = Path<TFieldValues>,
>({
methods,
name,
children,
...otherProps
}: FormComponentProps<TFieldValues, TName>) {
return (
<FormField
control={methods.control}
name={name}
render={() => (
<div {...otherProps}>
<FormItem>
{children}
<FormMessage />
</FormItem>
</div>
)}
/>
)
}