chrishoermann / zod-prisma-types

Generator creates zod types for your prisma models with advanced validation
Other
626 stars 47 forks source link

Type Error on `CreateManyUserInputEnvelopeSchema` #122

Closed andenacitelli closed 1 year ago

andenacitelli commented 1 year ago

I'm getting a type mismatch between the generated Prisma types and the generated Zod types. This is distinct from the issue Zod is currently having.

Steps:

zod: 3.21.1 zod-prisma-types: 2.5.4

Initially thought it was because of the type mismatch error in the most recent few zod versions, but no dice.

Seems to be related to me using the Omit functionality. Seems like the Prisma type retains those fields, but the Zod types do not. Example: /// @zod.custom.omit(["input"])

Schema:

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

generator zod {
  provider = "zod-prisma-types"
}

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

/// @@Gen.model(hide: true)
model CacheEntry {
  key   String @id @map("_id")
  value String
}

model User {
  // App-Agnostic
  id                 String  @id @default(auto()) @map("_id") @db.ObjectId
  email              String  @unique
  googleRefreshToken String? // TODO: Encrypt this or something

  // App-Specific
  tasks           Task[]
  ingestionEvents IngestionEvent[]
  epics           Epic[]
  projects        Project[]
  schedules       Schedule[]
  locations       Location[]
}

model IngestionEvent {
  /// <source, id> is a compound key
  id       String @id @default(auto()) @map("_id") @db.ObjectId
  source   String // Gmail, Google Calendar, etc.
  sourceId String // Always mapped from the source system
  url      String // The URL of the source event

  payload String // The data contained inside the event, represented as plaintext Markdown.

  createdTask Task?

  User      User?   @relation(fields: [userEmail], references: [email])
  userEmail String? @unique

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Task {
  // Important Metadata
  id          String   @id @default(auto()) @map("_id") @db.ObjectId
  completed   Boolean  @default(false)
  title       String /// @zod.string.min(1)
  size        TaskSize @default(MEDIUM)
  description String
  number      Int /// @zod.custom.omit(["input"])

  start DateTime? // Date at which this task can be worked on
  end   DateTime? // Day this task is due
  url   String? // Page that triggered this task to be created

  // Subtasks
  parentId String? @db.ObjectId
  parent   Task?   @relation(name: "TaskToTask-Subtasks", fields: [parentId], references: [id], onDelete: NoAction, onUpdate: NoAction)
  subtasks Task[]  @relation(name: "TaskToTask-Subtasks")

  // Dependents & Dependencies
  dependencies    Task[]   @relation("TaskToTask-Dependencies", fields: [dependenciesIds], references: [id])
  dependenciesIds String[] @db.ObjectId
  dependents      Task[]   @relation("TaskToTask-Dependencies", fields: [dependentsIds], references: [id])
  dependentsIds   String[] @db.ObjectId

  // Location
  location   Location? @relation(fields: [locationId], references: [id])
  locationId String?   @db.ObjectId

  // Parent epic
  Epic   Epic?   @relation(fields: [epicId], references: [id])
  epicId String? @unique @db.ObjectId

  // Parent project
  Project   Project? @relation(fields: [projectId], references: [id])
  projectId String?  @unique @db.ObjectId

  // Ingestion event that triggered this task to be created, if any
  ingestionEvent   IngestionEvent? @relation(fields: [ingestionEventId], references: [id])
  ingestionEventId String?         @unique @db.ObjectId

  User      User     @relation(fields: [userEmail], references: [email])
  userEmail String   @unique /// @zod.custom.omit(["input"])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Epic {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  name      String
  tasks     Task[]
  Project   Project? @relation(fields: [projectId], references: [id])
  projectId String?  @db.ObjectId

  User      User?    @relation(fields: [userEmail], references: [email])
  userEmail String?  @unique /// @zod.custom.omit(["input"])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Project {
  id    String @id @default(auto()) @map("_id") @db.ObjectId
  name  String
  epics Epic[]
  tasks Task[]

  User      User?    @relation(fields: [userEmail], references: [email])
  userEmail String?  @unique /// @zod.custom.omit(["input"])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Location {
  id       String @id @default(auto()) @map("_id") @db.ObjectId
  nickname String
  Tasks    Task[]

  User      User?    @relation(fields: [userEmail], references: [email])
  userEmail String?  @unique /// @zod.custom.omit(["input"])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Schedule {
  id        String     @id @default(auto()) @map("_id") @db.ObjectId
  timeslots Timeslot[]

  // Linkage
  User      User?   @relation(fields: [userEmail], references: [email])
  userEmail String? @unique /// @zod.custom.omit(["input"])

  // Audit
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Timeslot {
  id           String @id @default(auto()) @map("_id") @db.ObjectId
  startSeconds Int // # of Seconds Into Week
  endSeconds   Int // # of Seconds Into Week

  // Linkage
  Schedule   Schedule @relation(fields: [scheduleId], references: [id])
  scheduleId String   @db.ObjectId

  // Audit
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

/// JIRA-esque Task Statuses
enum TaskSize {
  SMALL
  MEDIUM
  LARGE
  EXTRA_LARGE
}

enum DayOfTheWeek {
  MONDAY
  TUESDAY
  WEDNESDAY
  THURSDAY
  FRIDAY
  SATURDAY
  SUNDAY
}

Error:

prisma/generated/zod/index.ts:3276:14 - error TS2322: Type 'ZodObject<{ data: ZodUnion<[ZodLazy<ZodType<Omit<TaskCreateManyUserInput, "number">, ZodTypeDef, Omit<TaskCreateManyUserInput, "number">>>, ZodArray<...>]>; }, "strict", ZodTypeAny, { ...; }, { ...; }>' is not assignable to type 'ZodType<TaskCreateManyUserInputEnvelope, ZodTypeDef, TaskCreateManyUserInputEnvelope>'.
  The types of '_type.data' are incompatible between these types.
    Type 'Omit<TaskCreateManyUserInput, "number"> | Omit<TaskCreateManyUserInput, "number">[]' is not assignable to type 'Enumerable<TaskCreateManyUserInput>'.
      Type 'Omit<TaskCreateManyUserInput, "number">' is not assignable to type 'Enumerable<TaskCreateManyUserInput>'.
        Property 'number' is missing in type 'Omit<TaskCreateManyUserInput, "number">' but required in type 'TaskCreateManyUserInput'.

3276 export const TaskCreateManyUserInputEnvelopeSchema: z.ZodType<Prisma.TaskCreateManyUserInputEnvelope> = z.object({
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  ../../../node_modules/.pnpm/@prisma+client@4.11.0_prisma@4.11.0/node_modules/.prisma/client/index.d.ts:15256:5
    15256     number: number
              ~~~~~~
    'number' is declared here.

prisma/generated/zod/index.ts:3846:14 - error TS2322: Type 'ZodObject<{ data: ZodUnion<[ZodLazy<ZodType<Omit<TaskCreateManyParentInput, "number" | "userEmail">, ZodTypeDef, Omit<TaskCreateManyParentInput, "number" | "userEmail">>>, ZodArray<...>]>; }, "strict", ZodTypeAny, { ...; }, { ...; }>' is not assignable to type 'ZodType<TaskCreateManyParentInputEnvelope, ZodTypeDef, TaskCreateManyParentInputEnvelope>'.
  The types of '_type.data' are incompatible between these types.
    Type 'Omit<TaskCreateManyParentInput, "number" | "userEmail"> | Omit<TaskCreateManyParentInput, "number" | "userEmail">[]' is not assignable to type 'Enumerable<TaskCreateManyParentInput>'.
      Type 'Omit<TaskCreateManyParentInput, "number" | "userEmail">' is not assignable to type 'Enumerable<TaskCreateManyParentInput>'.
        Type 'Omit<TaskCreateManyParentInput, "number" | "userEmail">' is missing the following properties from type 'TaskCreateManyParentInput': number, userEmail

3846 export const TaskCreateManyParentInputEnvelopeSchema: z.ZodType<Prisma.TaskCreateManyParentInputEnvelope> = z.object({
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

prisma/generated/zod/index.ts:4332:14 - error TS2322: Type 'ZodObject<{ data: ZodUnion<[ZodLazy<ZodType<Omit<TaskCreateManyEpicInput, "number" | "userEmail">, ZodTypeDef, Omit<TaskCreateManyEpicInput, "number" | "userEmail">>>, ZodArray<...>]>; }, "strict", ZodTypeAny, { ...; }, { ...; }>' is not assignable to type 'ZodType<TaskCreateManyEpicInputEnvelope, ZodTypeDef, TaskCreateManyEpicInputEnvelope>'.
  The types of '_type.data' are incompatible between these types.
    Type 'Omit<TaskCreateManyEpicInput, "number" | "userEmail"> | Omit<TaskCreateManyEpicInput, "number" | "userEmail">[]' is not assignable to type 'Enumerable<TaskCreateManyEpicInput>'.
      Type 'Omit<TaskCreateManyEpicInput, "number" | "userEmail">' is not assignable to type 'Enumerable<TaskCreateManyEpicInput>'.
        Type 'Omit<TaskCreateManyEpicInput, "number" | "userEmail">' is missing the following properties from type 'TaskCreateManyEpicInput': number, userEmail

4332 export const TaskCreateManyEpicInputEnvelopeSchema: z.ZodType<Prisma.TaskCreateManyEpicInputEnvelope> = z.object({
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

prisma/generated/zod/index.ts:4526:14 - error TS2322: Type 'ZodObject<{ data: ZodUnion<[ZodLazy<ZodType<Omit<TaskCreateManyProjectInput, "number" | "userEmail">, ZodTypeDef, Omit<TaskCreateManyProjectInput, "number" | "userEmail">>>, ZodArray<...>]>; }, "strict", ZodTypeAny, { ...; }, { ...; }>' is not assignable to type 'ZodType<TaskCreateManyProjectInputEnvelope, ZodTypeDef, TaskCreateManyProjectInputEnvelope>'.
  The types of '_type.data' are incompatible between these types.
    Type 'Omit<TaskCreateManyProjectInput, "number" | "userEmail"> | Omit<TaskCreateManyProjectInput, "number" | "userEmail">[]' is not assignable to type 'Enumerable<TaskCreateManyProjectInput>'.
      Type 'Omit<TaskCreateManyProjectInput, "number" | "userEmail">' is not assignable to type 'Enumerable<TaskCreateManyProjectInput>'.
        Type 'Omit<TaskCreateManyProjectInput, "number" | "userEmail">' is missing the following properties from type 'TaskCreateManyProjectInput': number, userEmail

4526 export const TaskCreateManyProjectInputEnvelopeSchema: z.ZodType<Prisma.TaskCreateManyProjectInputEnvelope> = z.object({
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

prisma/generated/zod/index.ts:4665:14 - error TS2322: Type 'ZodObject<{ data: ZodUnion<[ZodLazy<ZodType<Omit<TaskCreateManyLocationInput, "number" | "userEmail">, ZodTypeDef, Omit<TaskCreateManyLocationInput, "number" | "userEmail">>>, ZodArray<...>]>; }, "strict", ZodTypeAny, { ...; }, { ...; }>' is not assignable to type 'ZodType<TaskCreateManyLocationInputEnvelope, ZodTypeDef, TaskCreateManyLocationInputEnvelope>'.
  The types of '_type.data' are incompatible between these types.
    Type 'Omit<TaskCreateManyLocationInput, "number" | "userEmail"> | Omit<TaskCreateManyLocationInput, "number" | "userEmail">[]' is not assignable to type 'Enumerable<TaskCreateManyLocationInput>'.
      Type 'Omit<TaskCreateManyLocationInput, "number" | "userEmail">' is not assignable to type 'Enumerable<TaskCreateManyLocationInput>'.
        Type 'Omit<TaskCreateManyLocationInput, "number" | "userEmail">' is missing the following properties from type 'TaskCreateManyLocationInput': number, userEmail

4665 export const TaskCreateManyLocationInputEnvelopeSchema: z.ZodType<Prisma.TaskCreateManyLocationInputEnvelope> = z.object({
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Found 5 errors in the same file, starting at: prisma/generated/zod/index.ts:3276

Happy to follow up with more information if you need it!

andenacitelli commented 1 year ago

Really have no clue why TypeScript is treating this as an error. The type that the Envelope type wraps has those Omitted fields fine. It's saying that this:

export const TaskCreateManyLocationInputSchema: z.ZodType<Omit<Prisma.TaskCreateManyLocationInput, "number" | "userEmail">> = z.object({
  id: z.string().optional(),
  completed: z.boolean().optional(),
  title: z.string().min(1),
  size: z.lazy(() => TaskSizeSchema).optional(),
  description: z.string(),
  // omitted: number: z.number().int(),
  start: z.coerce.date().optional().nullable(),
  end: z.coerce.date().optional().nullable(),
  url: z.string().optional().nullable(),
  parentId: z.string().optional().nullable(),
  dependenciesIds: z.union([ z.lazy(() => TaskCreatedependenciesIdsInputSchema),z.string().array() ]).optional(),
  dependentsIds: z.union([ z.lazy(() => TaskCreatedependentsIdsInputSchema),z.string().array() ]).optional(),
  epicId: z.string().optional().nullable(),
  projectId: z.string().optional().nullable(),
  ingestionEventId: z.string().optional().nullable(),
  // omitted: userEmail: z.string(),
  createdAt: z.coerce.date().optional(),
 updatedAt: z.coerce.date().optional()
}).strict();

Doesn't work with this type:

export type TaskCreateManyLocationInput = {
    id?: string
    completed?: boolean
    title: string
    size?: TaskSize
    description: string
    number: number
    start?: Date | string | null
    end?: Date | string | null
    url?: string | null
    parentId?: string | null
    dependenciesIds?: TaskCreatedependenciesIdsInput | Enumerable<string>
    dependentsIds?: TaskCreatedependentsIdsInput | Enumerable<string>
    epicId?: string | null
    projectId?: string | null
    ingestionEventId?: string | null
    userEmail: string
    createdAt?: Date | string
    updatedAt?: Date | string
  }
andenacitelli commented 1 year ago

I have a suspicion that this is because of the pnpm workspace I'm working in. Will update if/as I make progress.

chrishoermann commented 1 year ago

@aacitelli just from reading the error message and the types I would say that this is because the prisma type expects the number and userEmail in the createMany method regardless of them being omitted in the zod type. Prisma does not know that the fields are omitted in the zod type and expectst them because they are required in the internal type.

did you add them manually in the data that is passed to the createMany method like

// lets assume 'inputData' is the data parsed via zod schema

const data = inputData.map(item => ({
  ...item, // actual zod parsed data
  number: 2, // value needs to be added manually because required in internal prisma type but omitted in zod type
  userEmail: "mail@mail.com", // same as above
)}

const createData = await prisma.task.createMany({
  data, // should now work without complaints
})

This is only a quick shot but I think this is your problem here.

andenacitelli commented 1 year ago

@chrishoermann I don't think the issue was with the library or Prisma, just my ts config. Running tsc on the output messed up for me, but the types work fine and don't report errors anywhere outside of the generated index file, so I'm fine with ignoring it.