react-hook-form / resolvers

📋 Validation resolvers: Yup, Zod, Superstruct, Joi, Vest, Class Validator, io-ts, Nope, computed-types, typanion, Ajv, TypeBox, ArkType, Valibot, effect-ts and VineJS
https://react-hook-form.com/
MIT License
1.77k stars 161 forks source link

Problem with yupResolver when one type in useForm generic is optional #575

Closed arminhupka closed 1 year ago

arminhupka commented 1 year ago

Describe the bug When we provide optional key in useForm generic we getting error TS2322: Type 'Resolver<{ name: string | null | undefined; age: number; }>' is not assignable to type 'Resolver<{ name: string; age?: number | undefined; }, any>'.   Types of parameters 'values' and 'values' are incompatible.     Type '{ name: string; age?: number | undefined; }' is not assignable to type '{ name: string | null | undefined; age: number; }'.       Types of property 'age' are incompatible.         Type 'number | undefined' is not assignable to type 'number'.           Type 'undefined' is not assignable to type 'number'.

To Reproduce Steps to reproduce the behavior:

Codesandbox link (Required) https://codesandbox.io/s/quirky-fog-3695px?file=/src/TestView.tsx

Expected behavior Works without problem

Screenshots image

jorisre commented 1 year ago

You don't need to manually type useForm from:

const { register } = useForm<{ name: string; age?: number }>({
    resolver: yupResolver(form)
  });

to

const { register } = useForm({
    resolver: yupResolver(form)
  });

Types are inferred from the schema

arminhupka commented 1 year ago

@jorisre ok good but how i can now access to form values? I just need pass interface which looks like our schema to SubmitHandler or there is some different way to do that?

jorisre commented 1 year ago

The schema is your single source of truth so

SubmitHandler<Yup.InferType<typeof form>>

https://github.com/jquense/yup#getting-started

jorisre commented 1 year ago

Here is the updated release note, sorry for the mistake, it should be at least a minor version instead of a patch

https://github.com/react-hook-form/resolvers/releases/tag/v3.1.1

khuongtp commented 1 year ago

I have a case that this auto infer feature seems not good at all. Before I can just pass the values and defaultValues conveniently like this image But now, TS keeps complaining, that's really annoying because we know that we pass somewhat the same type it expects image image

jorisre commented 1 year ago

Can you provide a CodeSandbox please?

sherpya commented 1 year ago

@khuongtp I've solved it by removing template argument for useForm but as I see you are not using it, make sure your version is 3.1.1 and maybe clean the project

khuongtp commented 1 year ago

Can you take a look at this? https://codesandbox.io/s/react-hook-form-apply-validation-ts-forked-58kw8x?file=/src/index.tsx

sherpya commented 1 year ago

Can you take a look at this? https://codesandbox.io/s/react-hook-form-apply-validation-ts-forked-58kw8x?file=/src/index.tsx

this sandbox is broken, also looks like codesandbox has problems with react 18 and typescript (it is unable to find react-dom/client import), please try with latest version locally:

    "@hookform/resolvers": "^3.1.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-hook-form": "^7.45.0",
    "yup": "^1.2.0"
asegarra commented 1 year ago

I got by this issue by passing a generic parameter to yupResolver with the infered type.

resolver: yupResolver<MyInferedType>(MySchema)

sourdenis commented 1 year ago

@asegarra somehow it doesn't work for me.

Here is the schema object const schema = Yup.object({ name: Yup.string().max(255).required(), });

the interface I use with useForm has more fields

Made a separate type for yupResolver type SchemaType = Yup.InferType<typeof schema>;

I use it like this: useForm<OperationFormValues>({ resolver: yupResolver<**SchemaType**>(schema), mode: 'all', defaultValues: { properties: {}, name: '', genericType: '', type: 0 }, });

and I get this kind of error: Type 'Resolver<{ name: string; }, any>' is not assignable to type 'Resolver<OperationFormValues, any>'. Types of parameters 'options' and 'options' are incompatible. Type 'ResolverOptions<OperationFormValues>' is not assignable to type 'ResolverOptions<{ name: string; }>'. Types of property 'names' are incompatible. Type 'string[] | undefined' is not assignable to type '"name"[] | undefined'. Type 'string[]' is not assignable to type '"name"[]'. Type 'string' is not assignable to type '"name"'.ts(2322)

Right now I had to revert to version 3.1.0 because for me the change made in 3.1.1 doesn't make any sense :)

kotarella1110 commented 1 year ago

resolved by https://github.com/react-hook-form/resolvers/pull/625 👍

SanderCokart commented 1 year ago

You don't need to manually type useForm from:

const { register } = useForm<{ name: string; age?: number }>({
    resolver: yupResolver(form)
  });

to

const { register } = useForm({
    resolver: yupResolver(form)
  });

Types are inferred from the schema

I honestly hate this change, I prefer to have typescript autocomplete inside the Yup.object because writing typescript is readable, by infering from a schema sucks especially in a pattern where I am prepopulating the form and only some data is required to be validated.

This would mean that if with this new format you do not define every single key even those that do not need validating breaks the inference of hook form, meaning register("name") gets no more autocomplete for things no defined in Yup schema. And sure you can do yupResolver<Yup.anyObject> but then I still will not have no autocomplete within the schema itself which is what I desire most because looking at the docs and knowing what key to navigate by looking vs my backender that can just send me a quicktype.io interface with the structure is very annoying.

I prefer my yup to infer from the form than the other way around.

aprilmintacpineda commented 1 year ago

We basically lost typings on our forms and now to get those typings back we need to add a workaround, and all I wanted to do was be able to use yup.lazy on yupResolver.

latobibor commented 1 year ago

I agree with @SanderCokart, it is a very valid case when you first define how your shapes are going to be, so you have type safety + IDE autocompletes (it is more than that actually) and only then add validation to fill in details that are not the responsibility of the type system.

Working against how IDEs work to help us is one of the lesser known anti-patterns there. As a code reviewer I would reject code that breaks IDE features, as it is the equivalent of turning off tests.