unlight / prisma-nestjs-graphql

Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module
MIT License
530 stars 77 forks source link

XXXUpdateWithoutYYY is not assignable to Prisma.XXXUpdateWithoutYYY #65

Open TMInnovations opened 2 years ago

TMInnovations commented 2 years ago

First, thank you for that awesome library! I love it!

Second: For some Entities I see "Problems" in the VSCode Problems-Panel. I'm glad it compiles without errors, but.. did anyone else have similar problems like I do (see beneath)? I

I can try to set up a minimal reproducable example next week if the problem isn't clear enough from the error description beneath. Please let me know if the effort is worth it ;-)

{
    "resource": "/c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/src/models/parcel/parcel.service.ts",
    "owner": "typescript",
    "code": "2322",
    "severity": 8,
    "message": "Type 'ParcelUpdateInput' is not assignable to type '(Without<ParcelUpdateInput, ParcelUncheckedUpdateInput> & ParcelUncheckedUpdateInput) | (Without<...> & ParcelUpdateInput)'.\n  Type 'ParcelUpdateInput' is not assignable to type 'Without<ParcelUncheckedUpdateInput, ParcelUpdateInput> & ParcelUpdateInput'.\n    Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").ParcelUpdateInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.ParcelUpdateInput'.\n      The types of 'labels.upsert' are incompatible between these types.\n        Type 'DalabelUpsertWithWhereUniqueWithoutParcelInput[]' is not assignable to type 'Enumerable<DalabelUpsertWithWhereUniqueWithoutParcelInput>'.\n          Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").DalabelUpsertWithWhereUniqueWithoutParcelInput[]' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.DalabelUpsertWithWhereUniqueWithoutParcelInput[]'.\n            Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").DalabelUpsertWithWhereUniqueWithoutParcelInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.DalabelUpsertWithWhereUniqueWithoutParcelInput'.\n              Types of property 'update' are incompatible.\n                Type 'DalabelUpdateWithoutParcelInput' is not assignable to type '(Without<DalabelUpdateWithoutParcelInput, DalabelUncheckedUpdateWithoutParcelInput> & DalabelUncheckedUpdateWithoutParcelInput) | (Without<...> & DalabelUpdateWithoutParcelInput)'.\n                  Type 'DalabelUpdateWithoutParcelInput' is not assignable to type 'Without<DalabelUncheckedUpdateWithoutParcelInput, DalabelUpdateWithoutParcelInput> & DalabelUpdateWithoutParcelInput'.\n                    Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").DalabelUpdateWithoutParcelInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.DalabelUpdateWithoutParcelInput'.\n                      The types of 'scan_dalabel_scans.upsert' are incompatible between these types.\n                        Type 'ScanUpsertWithWhereUniqueWithoutDalabelInput[]' is not assignable to type 'Enumerable<ScanUpsertWithWhereUniqueWithoutDalabelInput>'.\n                          Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").ScanUpsertWithWhereUniqueWithoutDalabelInput[]' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.ScanUpsertWithWhereUniqueWithoutDalabelInput[]'.\n                            Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").ScanUpsertWithWhereUniqueWithoutDalabelInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.ScanUpsertWithWhereUniqueWithoutDalabelInput'.\n                              Types of property 'update' are incompatible.\n                                Type 'ScanUpdateWithoutDalabelInput' is not assignable to type '(Without<ScanUpdateWithoutDalabelInput, ScanUncheckedUpdateWithoutDalabelInput> & ScanUncheckedUpdateWithoutDalabelInput) | (Without<...> & ScanUpdateWithoutDalabelInput)'.\n                                  Type 'ScanUpdateWithoutDalabelInput' is not assignable to type 'Without<ScanUncheckedUpdateWithoutDalabelInput, ScanUpdateWithoutDalabelInput> & ScanUpdateWithoutDalabelInput'.\n                                    Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").ScanUpdateWithoutDalabelInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.ScanUpdateWithoutDalabelInput'.\n                                      The types of 'parcel.upsert.update' are incompatible between these types.\n                                        Type 'ParcelUpdateWithoutScansInput' is not assignable to type '(Without<ParcelUpdateWithoutScansInput, ParcelUncheckedUpdateWithoutScansInput> & ParcelUncheckedUpdateWithoutScansInput) | (Without<...> & ParcelUpdateWithoutScansInput)'.\n                                          Type 'ParcelUpdateWithoutScansInput' is not assignable to type 'Without<ParcelUncheckedUpdateWithoutScansInput, ParcelUpdateWithoutScansInput> & ParcelUpdateWithoutScansInput'.\n                                            Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").ParcelUpdateWithoutScansInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.ParcelUpdateWithoutScansInput'.\n                                              The types of 'delivery.upsert.update' are incompatible between these types.\n                                                Type 'DeliveryUpdateWithoutParcelInput' is not assignable to type '(Without<DeliveryUpdateWithoutParcelInput, DeliveryUncheckedUpdateWithoutParcelInput> & DeliveryUncheckedUpdateWithoutParcelInput) | (Without<...> & DeliveryUpdateWithoutParcelInput)'.\n                                                  Type 'DeliveryUpdateWithoutParcelInput' is not assignable to type 'Without<DeliveryUncheckedUpdateWithoutParcelInput, DeliveryUpdateWithoutParcelInput> & DeliveryUpdateWithoutParcelInput'.\n                                                    Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").DeliveryUpdateWithoutParcelInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.DeliveryUpdateWithoutParcelInput'.\n                                                      Types of property 'branch' are incompatible.\n                                                        Type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/@prisma/client/nestjs-graphql/index\").BranchUpdateOneWithoutDelivery_branch_deliveriesInput' is not assignable to type 'import(\"c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index\").Prisma.BranchUpdateOneWithoutDelivery_branch_deliveriesInput'.",
    "source": "ts",
    "startLineNumber": 29,
    "startColumn": 47,
    "endLineNumber": 29,
    "endColumn": 51,
    "relatedInformation": [
        {
            "startLineNumber": 16915,
            "startColumn": 5,
            "endLineNumber": 16915,
            "endColumn": 9,
            "message": "The expected type comes from property 'data' which is declared here on type '{ select?: ParcelSelect; include?: ParcelInclude; data: (Without<ParcelUpdateInput, ParcelUncheckedUpdateInput> & ParcelUncheckedUpdateInput) | (Without<...> & ParcelUpdateInput); where: ParcelWhereUniqueInput; }'",
            "resource": "/c:/Users/tm/TmiCloud/2_TMI/Projects/2021/01_DEAD_LAS2/02_Software/backend/node_modules/.prisma/client/index.d.ts"
        }
    ]
}

BranchUpdateOneWithoutDelivery_branch_deliveriesInput from this library is:

export declare class BranchUpdateOneWithoutDelivery_branch_deliveriesInput {
    create?: InstanceType<typeof BranchCreateWithoutDelivery_branch_deliveriesInput>;
    connectOrCreate?: InstanceType<typeof BranchCreateOrConnectWithoutDelivery_branch_deliveriesInput>;
    upsert?: InstanceType<typeof BranchUpsertWithoutDelivery_branch_deliveriesInput>;
    connect?: InstanceType<typeof BranchWhereUniqueInput>;
    disconnect?: boolean;
    delete?: boolean;
    update?: InstanceType<typeof BranchUpdateWithoutDelivery_branch_deliveriesInput>;
}

BranchUpdateOneWithoutDelivery_branch_deliveriesInput from Prisma is:

  export type BranchUpdateOneWithoutDelivery_branch_deliveriesInput = {
    create?: XOR<BranchCreateWithoutDelivery_branch_deliveriesInput, BranchUncheckedCreateWithoutDelivery_branch_deliveriesInput>
    connectOrCreate?: BranchCreateOrConnectWithoutDelivery_branch_deliveriesInput
    upsert?: BranchUpsertWithoutDelivery_branch_deliveriesInput
    connect?: BranchWhereUniqueInput
    disconnect?: boolean
    delete?: boolean
    update?: XOR<BranchUpdateWithoutDelivery_branch_deliveriesInput, BranchUncheckedUpdateWithoutDelivery_branch_deliveriesInput>
  }

Part of the Prisma model is

generator nestjsgraphql {
  provider                              = "node node_modules/prisma-nestjs-graphql"
  fields_Validator_from                 = "class-validator"
  fields_Validator_input                = true
  requireSingleFieldsInWhereUniqueInput = true
  emitSingle                            = true
  emitCompiled                          = true
  purgeOutput                           = true
  output                                = "../node_modules/@prisma/client/nestjs-graphql"
}

model Parcel {
  id String @id @default(cuid())

  l Float?
  b Float?
  h Float?

  mass Float?

  // RELATIONS

  labels Dalabel[]

  delivery    Delivery? @relation(fields: [id_delivery], references: [id])
  id_delivery String?

  customer        Customer? @relation(fields: [customer_number], references: [number])
  customer_number String?

  surcharges Surcharge[]

  // INVERSE RELATIONS

  scans Scan[]

}

What I try and where I get the error is at:

import {ParcelUpdateInput} from '@prisma/client/nestjs-graphql';

update(where: ParcelWhereUniqueInput, updateParcelInput: ParcelUpdateInput) {
  return this.prisma.parcel.update({ where, data: updateParcelInput });
}

Thank you for taking time!

unlight commented 2 years ago

Sorry, but I cannot reproduce the error with default settings.

model Parcel {
  id String @id @default(cuid())

  l Float?
  b Float?
  h Float?

  mass Float?

  // RELATIONS

  labels Dalabel[]

  delivery    Delivery? @relation(fields: [id_delivery], references: [id])
  id_delivery String?

  customer        Customer? @relation(fields: [customer_number], references: [number])
  customer_number String?

  surcharges Surcharge[]

  // INVERSE RELATIONS

  scans Scan[]

}

model Scan {
  id       String  @id @default(cuid())
  Parcel   Parcel? @relation(fields: [parcelId], references: [id])
  parcelId String?
}

model Surcharge {
  id       String  @id @default(cuid())
  Parcel   Parcel? @relation(fields: [parcelId], references: [id])
  parcelId String?
}

model Delivery {
  id     String   @id @default(cuid())
  Parcel Parcel[]
}

model Customer {
  number String   @id @default(cuid())
  Parcel Parcel[]
}

model Dalabel {
  id       String  @id @default(cuid())
  Parcel   Parcel? @relation(fields: [parcelId], references: [id])
  parcelId String?
}

However, with requireSingleFieldsInWhereUniqueInput = true it gives

Property 'id' is missing in type '{}' but required in type 'ParcelWhereUniqueInput'

It break compatibility. I added note to documentation https://github.com/unlight/prisma-nestjs-graphql/blob/master/README.md#requiresinglefieldsinwhereuniqueinput

And I did not catch any errors related to data property:

import { ParcelUpdateInput } from '../../@generated';
import { ParcelWhereUniqueInput } from '../../@generated';

    const where: ParcelWhereUniqueInput = {};
    const updateParcelInput: ParcelUpdateInput = {};
    void $prisma.parcel.update({ where, data: updateParcelInput });
TMInnovations commented 2 years ago

Thank you for your answer. However, a strategy to overcome this problem is not very clear for me. Could you maybe explain the problem and a way to work around?

I had a free slot for creating a reproducible scenario. Please find it here: https://github.com/TMInnovations/prisma-nestjs-graphql-failing

It even breaks the application now. What needs to be changed to make the application work (or run at least ;-) )?

Edit: The behavior doesn't change with requireSingleFieldsInWhereUniqueInput in my case.

unlight commented 2 years ago

I thought that problem is requireSingleFieldsInWhereUniqueInput, but it is not. I able to reproduce it from repository you provided, unfortunately, it is not clear where is the root of issue.

TMInnovations commented 2 years ago

Thank you for testing!

I really want to help with this issue as I'm using this library for several of my projects. Is there anything I can help you with to resolve this bug? Do you have any thoughts about why this bug might happen?

https://github.com/TMInnovations/prisma-nestjs-graphql-failing/tree/try-to-get-it-working-1 In a new branch I tried to resolve the issue by first removing all /// @HideField directives => didn't work In the same branch I moved further and marked all possible fields of the schema as optional (?) => didn't work In the same branch I moved backwards and marked all possible fields as required (removed ?) => didn't work

Finally I removed the Address model and its connection to other models => building and running but the red lines and Errors in the "Problem" Section of VS-Code remain.

In another branch I will try to remove other models and evaluate if it fails or works. Any better idea how to tackle the issue? Help is very appreciated!

TMInnovations commented 2 years ago

It also builds when removing Country model. Still red lines and entries in Problem Section of VSCode https://github.com/TMInnovations/prisma-nestjs-graphql-failing/tree/try-to-get-it-working-2

TMInnovations commented 2 years ago

Removed all enums, didn't change anything, still failing https://github.com/TMInnovations/prisma-nestjs-graphql-failing/tree/try-to-get-it-working-3

TMInnovations commented 2 years ago

Also tried to move the output path of the prisma plugin => doesn't change anything

unlight commented 2 years ago

I was able to narrow down schema models to

model Customer {
  number              String        @id
  destinations        Destination[]
  collectors          Profile[]     @relation(name: "collector")
  favoriteDelivery    Delivery      @relation(fields: [id_favoriteDelivery], references: [id])
  id_favoriteDelivery String        @unique
  profile             Profile       @relation(fields: [id_profile], references: [id])
  id_profile          String        @unique
}

model Delivery {
  id                                  String       @id @default(cuid())
  destination                         Destination? @relation(fields: [id_destination], references: [id])
  id_destination                      String?      @unique
  customer_favouriteDelivery_customer Customer?
}

model Destination {
  id                            String    @id @default(cuid())
  customer_destination_customer Customer? @relation(fields: [customer_number], references: [number])
  customer_number               String?
  delivery_destination_delivery Delivery?
}

model Profile {
  id                         String    @id @default(cuid())
  customer                   Customer?
  collector_profile_customer Customer? @relation(name: "collector", fields: [customer_numbner], references: [number])
  customer_numbner           String?
}
{
    const where: G.CustomerWhereUniqueInput = { number: '1' };
    const updateCustomerInput: G.CustomerUpdateInput = {};
    void $prisma.customer.update({ where, data: updateCustomerInput });
}

If I delete anything from any model it will make tsc happy.

Looks like some kind of recursion of type check. Maybe it's limitation of typescript (it cannot resolve recursion references), l'll try to find related issue in https://github.com/microsoft/TypeScript/issues

TMInnovations commented 2 years ago

Hmm.. Any way to bypass this "maybe tsc bug" in the meantime (like disabling specific tsc rules)?

unlight commented 2 years ago

Any way to bypass this

You can skip type check here, by converting to any and then to Prisma.CustomerUpdateInput or you can create new variable with type Prisma.CustomerUpdateInput and assign primitive properties from Generated.CustomerUpdateInput There are some tools: class-transformer (classtoplain/plainToClass), nartc/mapper

P.S.

    let updateCustomerInput: G.CustomerUpdateInput = {};
 // error TS2321: Excessive stack depth comparing types 'ProfileUpdateOneRequiredWithoutCustomerInput | undefined' and 'ProfileUpdateOneRequiredWithoutCustomerInput | undefined'
    let p_updateCustomerInput: Prisma.CustomerUpdateInput = {
        profile: updateCustomerInput.profile,
    };

A lot of unresolved issues - https://github.com/microsoft/TypeScript/issues?q=is%3Aissue+sort%3Aupdated-desc+Excessive+stack+depth+comparing+types Looks like it the source of incompatibility issue.

TMInnovations commented 2 years ago

Thank you for the quick response. So the next step is to wait until the ts issues are resolved?

unlight commented 2 years ago

I doubt that it will be resolved soon.

TMInnovations commented 2 years ago

News!

If i add @default(cuid()) to the Customer models @id "number" the initial problem repo works:

Very likely related to be related to: https://github.com/prisma/prisma/issues/10063 and also likely to be related to: https://github.com/prisma/prisma/issues/8776

The thing is: do not have id fields that are NOT autogenerated (@default()) and hidden via @HideField() at the same time. Problem is: I need a default different from cuid() and therefore do the generation of the customer.number in the CreateCustomer resolver. Types of customer.number of Prisma.ParcelUpdateInput and G.ParcelUpdateInput cannot be the same because the graphql input doesn't include the @id field although it's required by prisma, as it's not autogenerated. Does that make sense?

unlight commented 2 years ago

I did not get the idea of removing @default.

I almost sure that is TS issue https://github.com/microsoft/TypeScript/issues/29112 https://github.com/microsoft/TypeScript/issues/46631 https://github.com/microsoft/TypeScript/issues/34933

People are saying that downgrading to typescript@3.5.3 solves the issue, but prisma requires typescript 4+

TMInnovations commented 2 years ago

The idea of removing @default is: A customer number should be a string made up of 10 random decimal digits (ex. "1234567890"). cuid() does provide the "random-ness" but not the format that is needed for a customer number. So the customer number cannot be auto-generated with cuid(). As of now I have the customer number created inside a createCustomer mutation resolver (graphql) which then assigns the generated number.

unlight commented 2 years ago

@TMInnovations I can suggest solution to solve the issue, but it require patch typescript.

  1. Find tsc.js and following source of code image
  2. Increase targetDepth (or sourceDepth) I was able to get rid of error "Excessive stack depth comparing types" in this schema by changing targetDepth = 200

To automate patching (CI/CD, typescript update), you can use https://github.com/ds300/patch-package

Note: It will fix only cli check, e.g. tsc --noEmit, to fix errors in your IDE or 3rd party libs which using typescript API, you probably need patch all files in typescript/lib/*.

Meanwhile looks like https://github.com/microsoft/TypeScript/issues/34933 this issue is most active in discussing this behavior.

DennieMello commented 2 years ago

Similar problem. Error is highlighted in vscode but project runs

jasonmacdonald commented 2 years ago

Does anyone have a better solution for this than patching Typescript?

BrayhanV commented 1 year ago

Anyone got found a solution for this?