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

Problem with validation of identical fields in the schema #3752

Open
gorinovsoft opened this issue Sep 12, 2024 · 4 comments
Open

Problem with validation of identical fields in the schema #3752

gorinovsoft opened this issue Sep 12, 2024 · 4 comments

Comments

@gorinovsoft
Copy link

Currently, there is an issue in the schema where, if identical fields are present in the user's schemas, Zod incorrectly validates the second testProperty field. Is there a way to fix this? At the moment, the only solution I've found is to change the field name (testProperty) to a unique one.

https://codesandbox.io/p/sandbox/zodschemas-forked-76tlcr?file=%2Fsrc%2FApp.tsx
Снимок экрана 2024-09-12 115322

`import { useCallback, VFC } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

const stringSchema = z.string().min(1, { message: "fieldIsRequired" });

const firstUser = z.object({
firstName: stringSchema,
lastName: stringSchema,
testProperty: stringSchema,
});

const secondUser = z.object({
firstName: stringSchema,
lastName: stringSchema,
testProperty: stringSchema.or(z.string().length(0)),
testPropertySecond: stringSchema.or(z.string().length(0)),
});

const UserSchema = z.union([firstUser, secondUser]);

const usersSchema = z.object({
users: UserSchema.array(),
});

type UsersForm = z.infer;

export const App: VFC = () => {
const { register, handleSubmit, formState, control } = useForm({
resolver: zodResolver(usersSchema),
defaultValues: {
users: [
{
firstName: "",
lastName: "",
testProperty: "",
},
{
firstName: "",
lastName: "",
testProperty: "",
testPropertySecond: "",
},
],
},
});

const errors = formState.errors;

console.log(errors, formState, control);

const onSubmit: SubmitHandler = useCallback(async (value) => {},
[]);

return (
<form
onSubmit={handleSubmit(onSubmit)}
style={{
display: "flex",
flexDirection: "column",
width: 512,
margin: "0 auto",
}}
>

First name first user
<input {...register("users.0.firstName")} />
{errors?.users?.[0]?.firstName?.message}


Last name first user
<input {...register("users.0.lastName")} />
{errors?.users?.[0]?.lastName?.message}


Test property first user
<input {...register("users.0.testProperty")} />
{errors?.users?.[0]?.testProperty?.message}

  <label style={{ marginTop: "40px" }}>
    <span>First name second user</span>
    <input {...register("users.1.firstName")} />
    <span>{errors?.users?.[1]?.firstName?.message}</span>
  </label>
  <label>
    <span>Last name second user</span>
    <input {...register("users.1.lastName")} />
    <span>{errors?.users?.[1]?.lastName?.message}</span>
  </label>
  <label>
    <span>Test property second user</span>
    <input {...register("users.1.testProperty")} />
    <span>{errors?.users?.[1]?.testProperty?.message}</span>
  </label>
  <label>
    <span>Second test property second user</span>
    <input {...register("users.1.testPropertySecond")} />
    <span>{errors?.users?.[1]?.testPropertySecond?.message}</span>
  </label>
  <button type="submit">Submit</button>
</form>

);
};
`

@sunnylost
Copy link

You can switch firstUser and secondUser position in union.

const UserSchema = z.union([secondUser, firstUser]);

Zod will iterate through all the options in a union array. If any of the options pass validation, the entire result is considered valid. However, if any of the options fail validation, it will return the result of the first invalid option and ignore the rest.

For example, if you use z.union([firstUser, secondUser]) to parse { firstName: "", lastName: "", testProperty: "" }, the firstUser schema will raise 3 errors, so that becomes the result. Even if you try parsing { firstName: "", lastName: "", testProperty: "", testPropertySecond: "" }, the outcome remains the same. This happens because firstUser is the first option evaluated, and Zod will return its result, ignoring the secondUser schema.

@Amirika18
Copy link

Amirika18 commented Sep 13, 2024

@sunnylost it could be a working solution, but if we change the order of zod schemas in the array and then decide to add a property with the same key but with different validation schema, validation will raise error in the testPropertySecond property. Take a look at this example below:
image

@sunnylost
Copy link

So the issue is that you want to use the firstUser schema to parse the first user and the secondUser schema for the second, but since both users have the same structure, there's no way to determine which schema to apply. In my opinion, each user should have a property to distinguish them, allowing you to use a discriminated union for validation.

@Amirika18
Copy link

So the issue is that you want to use the firstUser schema to parse the first user and the secondUser schema for the second, but since both users have the same structure, there's no way to determine which schema to apply. In my opinion, each user should have a property to distinguish them, allowing you to use a discriminated union for validation.

Yes, you are right! We want to apply the schema to object depends on some key (for example, id from enum). Unfortunately, zod union do not support the determination of validation schema according to key property and its value. We have z.discriminatedUnion which could help if it could work with transformed zod schemas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants