zod icon indicating copy to clipboard operation
zod copied to clipboard

Prisma XOR with Zod > 3.21.1 not working

Open chrishoermann opened this issue 2 years ago • 18 comments

We encounterd a bug when using generated prisma types with versions greater than 3.21.1.

You can reproduce the issue in the following repo: https://github.com/chrishoermann/zod-prisma-types-bug

As you can read in the following discussion we first thought it was an issue with prisma, but after digging around and finding that the suspected type has been this way for a long time now @njdowdy found that this is a zod issue that happend somwhere in 3.21.2

Discussed in https://github.com/colinhacks/zod/discussions/2171

Originally posted by chrishoermann March 9, 2023 I've built a generator for prisma (zod-prisma-types) that throws a type error since prisma updated their XOR type in 4.11:

  type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };

  /**
   * XOR is needed to have a real mutually exclusive union type
   * https://stackoverflow.com/questions/42123407/does-typescript-support-mutually-exclusive-types
   */
  type XOR<T, U> =
    T extends object ?
    U extends object ?
      (Without<T, U> & U) | (Without<U, T> & T)
    : U : T

before this type update the following zod schema worked without problems:

export const UserCreateInputSchema: z.ZodType<Prisma.UserCreateInput> = z.object({
  id: z.string().cuid().optional(),
  email: z.string().email({ message: "Invalid email address" }),
  name: z.string().min(1).max(100).optional().nullable(),
  role: z.union([ z.lazy(() => UserCreateroleInputSchema),z.lazy(() => RoleSchema).array() ]).optional(),
  enum: z.lazy(() => AnotherEnumSchema).optional(),
  scalarList: z.union([ z.lazy(() => UserCreatescalarListInputSchema),z.string().array() ]).optional(),
  posts: z.lazy(() => PostCreateNestedManyWithoutAuthorInputSchema).optional(),
  profile: z.lazy(() => ProfileCreateNestedOneWithoutUserInputSchema).optional(),
  location: z.lazy(() => LocationCreateNestedOneWithoutUserInputSchema).optional(),
}).strict();

export const UserUncheckedCreateInputSchema: z.ZodType<Prisma.UserUncheckedCreateInput> = z.object({
  id: z.string().cuid().optional(),
  email: z.string().email({ message: "Invalid email address" }),
  name: z.string().min(1).max(100).optional().nullable(),
  role: z.union([ z.lazy(() => UserCreateroleInputSchema),z.lazy(() => RoleSchema).array() ]).optional(),
  enum: z.lazy(() => AnotherEnumSchema).optional(),
  scalarList: z.union([ z.lazy(() => UserCreatescalarListInputSchema),z.string().array() ]).optional(),
  lat: z.number(),
  lng: z.number(),
  posts: z.lazy(() => PostUncheckedCreateNestedManyWithoutAuthorInputSchema).optional(),
  profile: z.lazy(() => ProfileUncheckedCreateNestedOneWithoutUserInputSchema).optional(),
}).strict();

export const UserCreateArgsSchema: z.ZodType<Prisma.UserCreateArgs> = z.object({
  select: UserSelectSchema.optional(),
  include: UserIncludeSchema.optional(),
  data: z.union([ UserCreateInputSchema,UserUncheckedCreateInputSchema ]),
}).strict()

so a simple union was sufficient to satisfy typescript. in Prisma 4.11 typescript now throws the following error because, as I see it, the Without<...> type can not be satisfied with a union anymore:

Type 'ZodObject<{ select: ZodOptional<ZodType<UserSelect, ZodTypeDef, UserSelect>>; include: ZodOptional<ZodType<UserInclude, ZodTypeDef, UserInclude>>; data: ZodUnion<...>; }, "strict", ZodTypeAny, { ...; }, { ...; }>' is not assignable to type 'ZodType<UserCreateArgs, ZodTypeDef, UserCreateArgs>'.
  The types of '_type.data' are incompatible between these types.
    Type '(UserCreateInput | UserUncheckedCreateInput) & (UserCreateInput | UserUncheckedCreateInput | undefined)' is not assignable to type '(Without<UserCreateInput, UserUncheckedCreateInput> & UserUncheckedCreateInput) | (Without<...> & UserCreateInput)'.
      Type 'UserCreateInput & UserUncheckedCreateInput' is not assignable to type '(Without<UserCreateInput, UserUncheckedCreateInput> & UserUncheckedCreateInput) | (Without<...> & UserCreateInput)'.
        Type 'UserCreateInput & UserUncheckedCreateInput' is not assignable to type 'Without<UserUncheckedCreateInput, UserCreateInput> & UserCreateInput'.
          Type 'UserCreateInput & UserUncheckedCreateInput' is not assignable to type 'Without<UserUncheckedCreateInput, UserCreateInput>'.
            Types of property 'lat' are incompatible.
              Type 'number' is not assignable to type 'undefined'.

I'm currently a bit stuck on how to solve this issue because I don't really know how to implement the Without<...> type especially because the z.ZodType<...> does not provide the object helper methods that would have been my first guess on how to approach this. Any help is appreciated.

chrishoermann avatar Mar 10 '23 21:03 chrishoermann

Thanks for creating the issue @chrishoermann ! I'm working zenstack and hit by the same thing 😄.

ymc9 avatar Mar 14 '23 01:03 ymc9

We have the same problem. Since almost 3 month now we are stuck to using zod in version 3.21.1. We put a resolutions entry at the top of our monorepo:

// package.json
{
  // ...
  "resolutions": {
    "zod": "<=3.21.1",
  }
}

cimchd avatar Jun 02 '23 05:06 cimchd

This issue has been here for a while 😄. Just wondering if it's on the radar for a fix soon.

ymc9 avatar Jun 04 '23 01:06 ymc9

It seems like this bug does not occur in v4.16.1 of Prisma 👍🏼

Intevel avatar Jun 26 '23 09:06 Intevel

@Intevel No, I can confirm same error with [email protected] and [email protected]

oceandrama avatar Jun 27 '23 09:06 oceandrama

@Intevel No, I can confirm same error with [email protected] and [email protected]

Did you updated your @prisma/client aswell?

Intevel avatar Jun 27 '23 09:06 Intevel

Did you updated your @prisma/client aswell?

Yes, sure

oceandrama avatar Jun 27 '23 09:06 oceandrama

Waiting for an update as well. Stuck here too

artem-alek avatar Jul 14 '23 23:07 artem-alek

any news on this one? Is it confirmed as a bug yet?

chrishoermann avatar Aug 30 '23 15:08 chrishoermann

If it's confirmed to be a bug, I'd like to try making a fix.

ymc9 avatar Aug 31 '23 10:08 ymc9

Hi @colinhacks , we need some attention here 😄. Many people build Prisma-related tools using zod and all likely blocked to some extent.

ymc9 avatar Sep 03 '23 12:09 ymc9

This issue needs to get more attention!

fer112233 avatar Oct 07 '23 07:10 fer112233

I do agree. I sadly have switched to dirzzle for new projetcs by now.

aronmal avatar Oct 07 '23 08:10 aronmal

Zod sure has a lot of open issues for a validation library. No wonder its the most used these days.

This is probably gonna take some time to get picked up without a random dev stepping in.

laneme avatar Nov 30 '23 18:11 laneme

Not only prisma, we recently encountered the same error with nextjs react-hook-forms zodResolver.

karthi-21 avatar Dec 03 '23 02:12 karthi-21

Hope this problem can be solved .

ksdaylight avatar Jan 05 '24 07:01 ksdaylight

Here to add some weight to the issue

Darkbound avatar Feb 24 '24 13:02 Darkbound

I've got that same problem. Is there any workaround in the meantime (that isn't downgrading)?

dominic-schmid avatar Mar 06 '24 16:03 dominic-schmid

We need this fixed :-)

CasperSK89 avatar Mar 13 '24 19:03 CasperSK89

Any news on this topic ? This is a blocking issue that prevent us to generate types from Prisma schema with latest Zod release

rickylabs avatar Mar 20 '24 16:03 rickylabs

setting up the following rule in tsconfig.json might be a solution.

{
    "compilerOptions": {
        "strictNullChecks": true   
    }
}

KenAdams-AK avatar Mar 22 '24 14:03 KenAdams-AK

When will the fix be released?

sergej2705 avatar Apr 05 '24 09:04 sergej2705

This fix has landed in zod@beta

colinhacks avatar Apr 18 '24 08:04 colinhacks