デリゲートすることで、ロジックを共有しつつ型を特化させる。
StringFormとNumberFormはそれぞれ文字列型/数値型のフィールドのみしか受け入れない入力フォームのコンポーネントになる。
import {
Control,
FieldPathByValue,
FieldValues,
PathValue,
useController,
useForm,
} from "react-hook-form";
const SubchildForm = <
T extends FieldValues,
P extends Path<T>,
V extends PathValue<T, P>,
>(props: {
control: Control<T>;
name: P;
defaultValue: V;
}) => {
const { field } = useController({
control: props.control,
name: props.name,
defaultValue: props.defaultValue,
});
return <input {...field}>hello</input>;
};
const StringForm = <
T extends FieldValues,
P extends FieldPathByValue<T, string | null | undefined>,
V extends PathValue<T, P>,
>(props: {
control: Control<T>;
name: P;
defaultValue: V;
}) => {
return (
<SubchildForm
control={props.control}
name={props.name}
defaultValue={props.defaultValue}
/>
);
};
const NumberForm = <
T extends FieldValues,
P extends FieldPathByValue<T, number | null | undefined>,
V extends PathValue<T, P>,
>(props: {
control: Control<T>;
name: P;
defaultValue: V;
}) => {
return (
<SubchildForm
control={props.control}
name={props.name}
defaultValue={props.defaultValue}
/>
);
};
type Schema = {
name: string;
job?: string | null;
age: number;
};
const Page = () => {
const form = useForm<Schema>({
defaultValues: {
age: 10,
},
});
return (
<div>
<StringForm control={form.control} name="job" defaultValue={""} />
<NumberForm control={form.control} name="age" defaultValue={0} />
</div>
);
};