Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zod resolver infered types are incorect since 4.1.0 #743

Open
vanwalj opened this issue Feb 15, 2025 · 15 comments · May be fixed by #753
Open

Zod resolver infered types are incorect since 4.1.0 #743

vanwalj opened this issue Feb 15, 2025 · 15 comments · May be fixed by #753
Assignees

Comments

@vanwalj
Copy link

vanwalj commented Feb 15, 2025

Describe the bug
zodResolver now infer schema type which is great but unfortunately produces invalid typings

To Reproduce
Setup a project using zod, typescript and react-hook-form, create a simple form such as

const FORM_SCHEMA = z.object({
  s: z.string(),
});

const App = () => {
  const form = useForm({
    resolver: zodResolver(FORM_SCHEMA),
  });

  const [s] = form.watch(["s"]);

  console.log(s);
  ...
};

Codesandbox link (Required)
https://codesandbox.io/p/sandbox/wispy-wildflower-fk7ymp?file=%2Fsrc%2Findex.js%3A11%2C4

Expected behavior
s is typed as string but should be typed as string | undefined

Screenshots
Image

Desktop (please complete the following information):

  • OS: macOS 15.3
  • Browser any
  • Versions: zod 3.24.2, react-hook-form 7.54.2, @hookform/resolvers 4.1.0

Additional context

I personally used to provide useForm both TFieldValues and TTransformedValues typings so that everything is properly typed such as

const form = useForm<
    DeepPartial<z.input<typeof SCHEMA>>,
    unknown,
    z.output<typeof SCHEMA>
  >({
    resolver: zodResolver(SCHEMA),
  });

but even this is now impossible since types are clashing: Types of parameters 'values' and 'values' are incompatible

Thank you 🙇

@ruiaraujo012
Copy link

I'm facing the same issue but for the valibot resolver, the cause is probably the same.

@an-tran511
Copy link

I have similar issue

@pgjones
Copy link

pgjones commented Feb 19, 2025

I think react-hook-form/react-hook-form#11951 is required before this repository can be updated in a similar manner to fix this bug.

Edit: I've written up how I type zod form validators here which should give context to why I think the input and output types are required in react-hook-form and in this repo.

@jorisre
Copy link
Member

jorisre commented Feb 19, 2025

Got it, thanks for reporting this. I'm looking into it.

@AnderssonPeter
Copy link

AnderssonPeter commented Feb 20, 2025

@jorisre I'm not 100% sure but woulden't it be more correct to use z.input instead of z.infer?
Let's say i have the following schema:

const schema = z
    .object({
        title: z
            .string()
            .optional()
            .transform(v => v ?? "")
            .pipe(
                z
                    .string()
                    .nonempty("Required"))
    });

    // { title: string; }
    type infer = z.infer<typeof schema>;
    // { title: string | undefined; }
    type input = z.input<typeof schema>;
    // { title: string; }
    type output = z.output<typeof schema>;

The idea here is that undefined is allowed as input value but not as output.

Not 100% sure this is the same issue but it might be?

@Omelyan

This comment has been minimized.

@diegotraid
Copy link

diegotraid commented Feb 24, 2025

Because the authors after the years still do not understand what a Form is. A Form is the transformation (and possibly validation) of an Input into an Output!

Why is this code logically absolutely correct, yet contains a TypeScript error?! Image

and, on the other hand, why does the following code, despite having no errors, not work?! Image

I'm over it. Just don't use the lib.

Maybe helping out the useForm function by definining input and output types would help:

const schema = z
  .object({
    id: z.number(),
  })
  .transform((v) => ({
    name: `my id is ${v.id}`,
  }));

type SchemaInput = z.input<typeof schema>;
type SchemaOutput = z.output<typeof schema>;

const form = useForm<SchemaInput, unknown, SchemaOutput>({
  resolver: zodResolver(schema),
  values: {
    id: 1, // No TS error since id is part of the input schema
  },
});

form.handleSubmit((output) => console.log(output))();

Outputs: {name: 'my id is 1'}

const schema = z
  .object({
    id: z.number(),
  })
  .transform((v) => ({
    name: `my id is ${v.id}`,
  }));

type SchemaInput = z.input<typeof schema>;
type SchemaOutput = z.output<typeof schema>;

const form = useForm<SchemaInput, unknown, SchemaOutput>({
  resolver: zodResolver(schema),
  values: {
    name: 'yeah right', // TS Error: Object literal may only specify known properties, and 'name' does not exist in type '{ id: number; }'
  },
});

form.handleSubmit((output) => console.log(output))(); // No output since submitting fails

@Omelyan

This comment has been minimized.

@jorisre
Copy link
Member

jorisre commented Feb 25, 2025

Hello everyone, as I mentioned earlier, I'm currently working on it. I kindly ask for your patience, and if you have suggestions, feel free to open a pull request instead of expressing dissatisfaction.

@bluebill1049
Copy link
Member

Thanks heap @jorisre for helping and looking into this issue 🙏

@AxelSun
Copy link

AxelSun commented Mar 11, 2025

Hello everyone, I ran into this issue when doing some upgrades of our ts and linter. Has anyone a set of versions for react-hook-form, zod and @hookform/resolvers where the types for watch work? I tried several versions for resolvers 3.x.x and 2.x.x bur can't make it work. See codesandbox: https://codesandbox.io/p/sandbox/q8hhln

@Omelyan

This comment was marked as spam.

@xsjcTony
Copy link

My use case is with the .transform() or .pipe()

At type level, the Resolver<> or UseFormProps<> should support both TFieldValues and TTransformedValues

@aryzing
Copy link

aryzing commented Mar 19, 2025

Not sure if related, but when trying to use Valibot and transforming a string to a number, like so,

const myFormSchema = v.object({
  foo: v.pipe(
    v.string(),
    v.transform((value) => Number(value))
  ),
});

there's a type error on the resolver,

Image

which reads:

Argument of type 'ObjectSchema<{ readonly foo: SchemaWithPipe<readonly [StringSchema<undefined>, TransformAction<string, number>]>; }, undefined>' is not assignable to parameter of type 'BaseSchema<{ foo: string; }, { foo: string; }, any> | BaseSchemaAsync<{ foo: string; }, { foo: string; }, any>'.
  Type 'ObjectSchema<{ readonly foo: SchemaWithPipe<readonly [StringSchema<undefined>, TransformAction<string, number>]>; }, undefined>' is not assignable to type 'BaseSchema<{ foo: string; }, { foo: string; }, any>'.
    Types of property ''~standard'' are incompatible.
      Type 'StandardProps<{ foo: string; }, { foo: number; }>' is not assignable to type 'StandardProps<{ foo: string; }, { foo: string; }>'.
        Type '{ foo: number; }' is not assignable to type '{ foo: string; }'.
          Types of property 'foo' are incompatible.
            Type 'number' is not assignable to type 'string'.

@jorisre
Copy link
Member

jorisre commented Mar 19, 2025

@aryzing It's related, working on a fix for all resolvers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment