I am new to Typescript and having troubles getting the types to work correctly when I have react-hook-form values as props.
For example, I have a generic Form component that I use for many forms.
interface Props {
reactHookForm: UseFormMethods<any>
onSubmit: SubmitHandler<any>
isDisabled?: boolean
isLoading?: boolean
isReadOnly?: boolean
children: ReactElement
}
const Form = ({ reactHookForm, onSubmit, isDisabled = false, isLoading = false, isReadOnly = false, children }: Props) => {
const { handleSubmit, register, errors, control, watch } = reactHookForm
return (
<Context.Provider value={{ register, errors, control, watch, isDisabled, isLoading, isReadOnly }}>
<form onSubmit={handleSubmit(onSubmit)} className="w-full">
{children}
</form>
</Context.Provider>
)
}
And in an example component I have:
const Auth = ({ onSignIn }: Props) => {
const signInForm = useForm({ defaultValues, resolver })
const onSubmit: SubmitHandler<FormData> = (values: FormData) => {
const { email, password } = values
const isAuthorized = (user: MyCustomUser) => {
// do stuff
}
const onCompleted = () => {
// do stuff
}
const onError = (error: MyCustomError) => {
// do stuff
}
signInWithEmailAndPassword({ email, password, onCompleted, onError, isAuthorized })
}
return (
<section className="flex h-full w-full justify-center items-center bg-gray-100 dark:bg-gray-800">
{/* some components... */}
<Form reactHookForm={signInForm} onSubmit={onSubmit} isLoading={attemptingSignIn}>
{/* form components */}
</Form>
{/* some more components ... */}
</section>
)
}
and with my FormData, defaultValues and resolver coming from file that looks something like:
import { Resolver } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import { object, SchemaOf, string } from "yup"
import { createInputRequiredError } from "Components/Form/utils"
type FormData = {
email: string
password: string
}
const defaultValues: FormData = { email: "", password: "" }
const schema: SchemaOf<FormData> = object().shape({
email: string().required(createInputRequiredError("email")),
password: string().required(createInputRequiredError("password"))
})
const resolver = yupResolver(schema)
export type { FormData }
export { defaultValues, resolver }
My problem is that I have to use reactHookForm: UseFormMethods<any> and onSubmit: SubmitHandler<any> with the any type. React hook form has a FieldValues and I thought I could use this by having those types as reactHookForm: UseFormMethods and onSubmit: SubmitHandler<FieldValues> however I get the following error:
Types of parameters 'data' and 'data' are incompatible.
Type '{ [x: string]: any; }' is missing the following properties from type '{ email: string; password: string; }': email, password
How can I set up the reactHookForm and onSubmit types so that they can be generic to any valid react-hook-form values?
Update
After upgrading my package to the latest version of react-hook-form:7.3.0 I now have a slightly different message (I think these are still equivalent problems).
Type 'UseFormReturn<FormData>' is not assignable to type 'UseFormReturn<FieldValues>'.
Types of property 'setError' are incompatible.
Type 'UseFormSetError<FormData>' is not assignable to type 'UseFormSetError<FieldValues>'.
My Form provider was slightly update, it now looks like:
interface Props {
reactHookForm: UseFormReturn<any>
onSubmit: SubmitHandler<any>
isDisabled?: boolean
isLoading?: boolean
isReadOnly?: boolean
children: ReactElement | ReactElement[] | string
}
const Form = ({ reactHookForm, onSubmit, isDisabled = false, isLoading = false, isReadOnly = false, children }: Props) => {
const {
handleSubmit,
register,
formState: { errors },
control,
watch,
getValues,
setValue
} = reactHookForm
return (
<Context.Provider value={{ register, errors, control, watch, getValues, setValue, isDisabled, isLoading, isReadOnly }}>
<form onSubmit={handleSubmit(onSubmit)} className="w-full">
{children}
</form>
</Context.Provider>
)
}