remix-hook-form
remix-hook-form copied to clipboard
TypeScript: Return type of useRemixForm hook is incorrect.
Hi, Thanks for building this package! I'm trying to migrate my app from remix-validated-form to this one but I've run into some issue regarding TS types.
Loos like useRemixForm is incorrectly returning form types.
Here's the type returned from react-hook-form:
...and here's remix-hook-form:
You can see the values are incorrectly typed as undefined. According to the zod schema, those fields should never be undefined just like in the first example.
const schema = z.object({
name: z.string().trim().min(3).max(32),
slug: z.string().trim().min(3).max(32),
})
hi @piotrkulpinski, I'll look into this but you can pass in the schema type as a workaround and it will work correctly, eg. useRemixForm<YourSchema>()
hi @piotrkulpinski, I'll look into this but you can pass in the schema type as a workaround and it will work correctly, eg.
useRemixForm<YourSchema>()
Tried that, but this has given me even bigger problems with typing the models correctly.
same happens when I want to pass register to a component. using UseFormRegister<FormData> doesn't work:
Type '(name: "method" | "email" | "phone", options?: (RegisterOptions<{ method: "whatsapp" | "email"; email: string; phone: string; }> & { disableProgressiveEnhancement?: boolean | undefined; }) | undefined) => { ...; }' is not assignable to type 'UseFormRegister<{ method: "whatsapp" | "email"; email: string; phone: string; }>'.
Types of parameters 'options' and 'options' are incompatible.
Type 'RegisterOptions<{ method: "whatsapp" | "email"; email: string; phone: string; }, TFieldName> | undefined' is not assignable to type '(RegisterOptions<{ method: "whatsapp" | "email"; email: string; phone: string; }> & { disableProgressiveEnhancement?: boolean | undefined; }) | undefined'.
Type 'Partial<{ required: string | ValidationRule<boolean>; min: ValidationRule<string | number>; max: ValidationRule<string | number>; ... 9 more ...; deps: string | string[]; }> & { ...; }' is not assignable to type '(RegisterOptions<{ method: "whatsapp" | "email"; email: string; phone: string; }> & { disableProgressiveEnhancement?: boolean | undefined; }) | undefined'.
Type 'Partial<{ required: string | ValidationRule<boolean>; min: ValidationRule<string | number>; max: ValidationRule<string | number>; ... 9 more ...; deps: string | string[]; }> & { ...; }' is not assignable to type 'Partial<{ required: string | ValidationRule<boolean>; min: ValidationRule<string | number>; max: ValidationRule<string | number>; ... 9 more ...; deps: string | string[]; }> & { ...; } & { ...; }'.
Type 'Partial<{ required: string | ValidationRule<boolean>; min: ValidationRule<string | number>; max: ValidationRule<string | number>; ... 9 more ...; deps: string | string[]; }> & { ...; }' is not assignable to type 'Partial<{ required: string | ValidationRule<boolean>; min: ValidationRule<string | number>; max: ValidationRule<string | number>; ... 9 more ...; deps: string | string[]; }>'.
Types of property 'validate' are incompatible.
Type 'Validate<TFieldName extends `${infer K}.${infer R}` ? K extends "method" | "email" | "phone" ? R extends Path<{ method: "whatsapp" | "email"; email: string; phone: string; }[K]> ? PathValue<...> : never : K extends `${number}` ? never : never : TFieldName extends "method" | ... 1 more ... | "phone" ? { ...; }[TField...' is not assignable to type 'Validate<string, { method: "whatsapp" | "email"; email: string; phone: string; }> | Record<string, Validate<string, { method: "whatsapp" | "email"; email: string; phone: string; }>> | undefined'.
Type 'Validate<TFieldName extends `${infer K}.${infer R}` ? K extends "method" | "email" | "phone" ? R extends Path<{ method: "whatsapp" | "email"; email: string; phone: string; }[K]> ? PathValue<...> : never : K extends `${number}` ? never : never : TFieldName extends "method" | ... 1 more ... | "phone" ? { ...; }[TField...' is not assignable to type 'Validate<string, { method: "whatsapp" | "email"; email: string; phone: string; }> | Record<string, Validate<string, { method: "whatsapp" | "email"; email: string; phone: string; }>> | undefined'.
Type 'Validate<TFieldName extends `${infer K}.${infer R}` ? K extends "method" | "email" | "phone" ? R extends Path<{ method: "whatsapp" | "email"; email: string; phone: string; }[K]> ? PathValue<...> : never : K extends `${number}` ? never : never : TFieldName extends "method" | ... 1 more ... | "phone" ? { ...; }[TField...' is not assignable to type 'Validate<string, { method: "whatsapp" | "email"; email: string; phone: string; }>'.
Type 'string' is not assignable to type 'TFieldName extends `${infer K}.${infer R}` ? K extends "method" | "email" | "phone" ? R extends Path<{ method: "whatsapp" | "email"; email: string; phone: string; }[K]> ? PathValue<...> : never : K extends `${number}` ? never : never : TFieldName extends "method" | ... 1 more ... | "phone" ? { ...; }[TFieldName] : T...'.ts(2322)
SignInMethodSelector.tsx(20, 3): The expected type
Hmm can you guys install the latest versions of remix-hook-form and react-hook-form and let me know if that resolves it? It could be a mismatch between the versions, but what is important to note is that the return types ARE different, there are a few changes added on top of react-hook-form so they can't be identical, like handleSubmit, register, reset etc are enhanced for remix use-cases
Hi @AlemTuzlak, any way you can expose the equivalent type of UseFormReturn which remix-hook-form is using? I'm passing the form object to a custom hook so no way to init the form there.
export function useSomeCustomHook(
setSomeState: React.Dispatch<React.SetStateAction<boolean>>,
form: UseFormReturn,
) {...}
I'm using the latest versions of react-hook-form & remix-hook-form.
Running into the same issue. I have the latest versions of both react-hook-form and remix-hoko-form.
Here's the error I am seeing:
Type '{ children: Element; handleSubmit: (e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>; reset: (values?: { email: string; password: string; } | { ...; } | undefined, options?: Partial<...> | undefined) => void; ... 12 more ...; setFocus: UseFormSetFocus<...>; }' is not assignable to type 'UseFormReturn<{ email: string; password: string; }, any, undefined>'.
Types of property 'reset' are incompatible.
Type '(values?: { email: string; password: string; } | { email?: string | undefined; password?: string | undefined; } | undefined, options?: Partial<{ keepDirtyValues: boolean; keepErrors: boolean; keepDirty: boolean; ... 7 more ...; keepSubmitCount: boolean; }> | undefined) => void' is not assignable to type 'UseFormReset<{ email: string; password: string; }>'.
Types of parameters 'values' and 'values' are incompatible.
Type '{ email: string; password: string; } | { email?: string | undefined; password?: string | undefined; } | ResetAction<{ email: string; password: string; }> | undefined' is not assignable to type '{ email: string; password: string; } | { email?: string | undefined; password?: string | undefined; } | undefined'.
Type 'ResetAction<{ email: string; password: string; }>' is not assignable to type '{ email: string; password: string; } | { email?: string | undefined; password?: string | undefined; } | undefined'.ts(2322)
My code:
const schema = z.object({
email: z
.string({ required_error: "Email is required" })
.email("Email is invalid"),
password: z.string({ required_error: "Password is required" }),
});
type FormData = z.infer<typeof schema>;
// Form component is from shadcn
export default function Login() {
const form = useRemixForm<FormData>({
mode: "onSubmit",
resolver,
});
return (
<FormProvider {...form}>
<form onSubmit={form.handleSubmit} className="space-y-8">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</FormProvider>
);
}
Update: fixing the above error by manually setting reset, gives another error:
Conversion of type '(e?: BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>' to type 'UseFormHandleSubmit<{ email: string; password: string; }, { email: string; password: string; }>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Types of parameters 'e' and 'onValid' are incompatible.
Type 'SubmitHandler<{ email: string; password: string; }>' is not comparable to type 'BaseSyntheticEvent<object, any, any>'.ts(2352)
@makrandgupta It seems you're using the FormProvider from react-hook-form the two are not interchangeable as the remix-hook-form package augments certain functions to allow for easier handling in Remix.
@usr3 I now export UseRemixFormReturn type
a lot of issues arise from the fact that remix-hook-form has different types compared to react-hook-form, I'm closing this for now as this was opened a while ago and I've changed the signatures of many of these functions, please feel free to open new issues!