Prisma1: Prisma client: incorrect TS types for nullable field

Created on 27 Dec 2018  路  29Comments  路  Source: prisma/prisma1

Hey guys.
I just noticed that generated TS types for SomeTypeUpdateInput doesn't allow null for fields that are nullable.
E.g.:
datamodel.graphql:

type User {
  id: ID! @unique
  nickname: String
}

Trying to update nickname to null:

prisma.updateUser({
  where: { id: 'abcdef' },
  data: { nickname: null },
});

Executing this code works and have desired outcome, however TypeScript complains:

Type 'null' is not assignable to type 'string | undefined'

I guess this is bug in generated types?
Thanks.

bu2-confirmed rf0-needs-spec areclients kinbug

Most helpful comment

This is preventing prisma-client from being used with any type-safe graphql library when strict is enabled, such as graphqlgen, graphql-code-generator, nexus or nexus-prisma

All 29 comments

Hi @dodas,

I can't reproduce this behavior in the latest version of prisma.

As seen in the screenshot everything is fine:-
image
image

Here are the versions I am using:

  1. Prisma server: 1.23 (should have no effect though)
  2. Prisma cli: prisma/1.23.2 (darwin-x64) node-v11.5.0 (check this one)
  3. prisma-client-lib: 1.23.2

I updated both prisma-client-lib and global prisma cli to latest versions, regenerated client, restarted TS language server in VSCode and still having same issue.
I am not an TS expert, but I guess that when interface says String (which is just alias of string), it cannot accept null.

~No that is not the case. The ? operator makes it nullable.~

If your project is open source or you can put together a minimal reproduction, please share it.

Okay, I think I found the reason why I am getting error and you don't.
In your tsconfig.json, try setting "strict": true under "compilerOptions" and we should probably have same result.

Note that ? operator makes an property _optional_, not _nullable_. That is, you can omit it altogether, but not pass null or undefined.

So I think prisma should generate nullable types as String | null to allow using TS in strict mode.

Thanks for the reply! Indeed ? makes things optional 鈽猴笍. Null and undefined is assignable to every type that is optional in non strict mode. That is why I was pointing at that in the first place.

Main reason here is strictNullCheck and how TS handles null differently when this option is enabled.

I just tried omitting an nullable property and it doesn't update it to null - and honestly, that would be very surprising and dangerous behaviour.
Until this is resolved, I am just using // @ts-ignore before line that is passing null.

I thought you were using it in create. 馃槄

Indeed this needs a proper resolution now.

We will look further into this after the holidays

Any update on this?
Just upgraded Prisma from 1.20.7 to 1.24 and again something is broken. not a nice dev experience. 馃槙
Can anyone tell at which version this first popped up?

Casting from graphqlgen generated types to prisma-client types helps temporarily.

I have the same error

const user = await db.createUser({
  settings: {
    create: {
      newsletter: settings.newsletter,
    },
  },
});

Error message

Type '{ create: { newsletter: boolean | null | undefined; updates: true | undefined; }; }' is not assignable to type 'UserSettingCreateOneWithoutUserInput'.

Newsletter is optional on apollo so it can be null, the problem is that I can't pass null to newsletter because is a required boolean.

Could be possible to treat null as undefined on the client?

Hi! I've been trying to solve this via graphqlgen. Here's the issue on that repo

At this point the issue becomes about Prisma, not graphqlgen. Correct me if I am wrong but I believe the issue you are facing is that the Prisma lib param types will not accept undefined.

There are two possible fixes the prisma team can do:

  1. use GraphQL default feature, e.g.:
    graphql input PostWhereInput { AND: [PostWhereInput!] = null
  2. change their prisma lib to accept undefined

Hi! I've been trying to solve this via graphqlgen. Here's the issue on that repo

At this point the issue becomes about Prisma, not graphqlgen. Correct me if I am wrong but I believe the issue you are facing is that the Prisma lib param types will not accept undefined.
There are two possible fixes the prisma team can do:

  1. use GraphQL default feature, e.g.:
    graphql input PostWhereInput { AND: [PostWhereInput!] = null
  2. change their prisma lib to accept undefined

The problem is not in the types, the problem is apollo server can accept null as undefined for the input of the resolver.

So if your datamodel doesn't allows null for a field you have to convert your null from your apollo server input into undefined so you don't get a runtime error.

  const user = await prisma.createUser({
    name,
    settings: {
      create: {
        newsletter: data.settings.newsletter || undefined,
      },
    },
  });

In the example above data is coming from the args of an apollo resolver, in the schema of the input the newsletter field is optional but in the datamodel is required so it can't be null.

So the types are fine and they are preventing you to make a runtime error.

So I think there is not an issue here, the behaviour is fine and there's not an error either in apollo or prisma, but is hard to get why null is not valid.

It's just that with this setup, the code gets extremely verbose. We have to check for the existence of every nested value before using it or append a ! to assert non-null and non-undefined.

...After having refreshed my TS knowledge and looked around abit, I'll just do it because it's what the TS team/community has been doing with strict mode.

I'll probably spend some time trying to find or figure out a smart way to 'go around' this verbosity if I ever need it. For now, I'm glad that things are actually working as they're supposed to. Thanks for the quick response @ellipticaldoor

I was wrong about what the issue is here in the thread over on graphqlgen side. It鈥檚 not that the param types of prisma client need to also accept undefined, but rather null.

So I don鈥檛 think the current Prisma param types are ok as is, rather I think it should _also_ accept explicit null for fields that are nullable. An important detail for usability.

Hey @pantharshit00 you added needs-spec label. The change required is trivial I think. But if you really need something formal I鈥檒l provide it. What do you need to see exactly?

Yes, maybe a solution could be that prisma treats null as undefined, not sure if that can cause conflicts in other place.

not sure if that can cause conflicts in other place.

It shouldn't because its input (not output) and it increases what's accepted, not reduces.

Its technically possible that the param types are being extracted for use else where (e.g. Params<prisma.doThing>) but I highly doubt that's actually happening.

This is preventing prisma-client from being used with any type-safe graphql library when strict is enabled, such as graphqlgen, graphql-code-generator, nexus or nexus-prisma

Any progress on this? :smile:

Going back to @dodas original issue, it also prevents proper queries. e.g. a user with no posts

prisma.users({
  where: { id: 'abcdef',  posts: undefined },
  data: { nickname: null },
});

will return all users, users with posts included.

prisma.users({
  where: { id: 'abcdef',  posts: null },
  data: { nickname: null },
});

Returns all users without posts, but TS yells Type null is not assignable to type PostWhereInput | undefined

While this is in progress, I've migrated my entire codebase to nexus-prisma and it works fine there.

So for anyone who'd like to keep their strong typing _and_ develop with Prisma and have auto-generation; nexus-prisma is the way to go. Nexus uses another paradigm though (code first), it took me about 2-4 hours to wrap my head around the benefits and way to do things. After that, I haven't looked back.

It's so amazing not having to maintain schema files and have everything written in code.

@MathiasKandelborg I am also using nexus, but this issue still exists.

Consider having the following schema:

export const UserWhereInput = inputObjectType({
  name: 'UserWhereInput',
  definition: t => {
    t.id('id', { required: false })
    t.string('email', { required: false })
  },
})

export const UserQuery = extendType({
  type: 'Query',
  definition: t => {
    t.list.field('users', {
      type: 'User',
      args: {
        where: arg({
          type: 'UserWhereInput',
          required: false,
        }),
      },
      resolve: async (root, args, ctx) => {
        try {
          return await ctx.prisma.users({ where: args.where }) // TS Error here
        } catch (err) {
          throw err
        }
      },
    })
  },
})

My custom UserWhereInput is not assignable to prismas UserWhereInput, because the args generated by nexus are type | null | undefined whereas the types of prisma are only type | undefined.

Type 'null' is not assignable to type 'UserWhereInput | undefined'.ts(2345)

@steida can you elaborate on how that works?

This has been fixed with prisma cli version 1.32.0-beta.1 :D

I will be in tomorrow's 1.32 release as well.

I dont see this issue as solved. Running on prisma-cli on 1.34.3 with nexus 0.12.0-beta.6 and still have to disable strict in tsconfig.json

@NikoMontana Can you please provide us with a reproduction repository?

Can you please provide us with a reproduction repository?

I'm having the same issue as well (I think) here's a reproduction example:

https://github.com/prisma/prisma-examples/compare/master...markstreich:strict_null

Screen Shot 2019-08-10 at 2 03 41 PM

tsc output:

src/index.ts:40:54 - error TS2345: Argument of type '{ after?: Maybe<string>; before?: Maybe<string>; first?: number | null | undefined; last?: number | null | undefined; orderBy?: "id_ASC" | "id_DESC" | "createdAt_ASC" | "createdAt_DESC" | ... 9 more ... | undefined; skip?: number | ... 1 more ... | undefined; where?: { ...; } | ... 1 more ... | undefined; }' is not assignable to parameter of type '{ where?: PostWhereInput | undefined; orderBy?: "id_ASC" | "id_DESC" | "createdAt_ASC" | "createdAt_DESC" | "updatedAt_ASC" | "updatedAt_DESC" | "published_ASC" | "published_DESC" | ... 4 more ... | undefined; ... 4 more ...; last?: number | undefined; }'.
  Types of property 'where' are incompatible.
    Type '{ AND?: ...[] | null | undefined; author?: { AND?: ...[] | null | undefined; email?: Maybe<string>; email_contains?: Maybe<string>; email_ends_with?: Maybe<string>; email_gt?: Maybe<string>; ... 42 more ...; posts_some?: ... | ... 1 more ... | undefined; } | null | undefined; ... 61 more ...; updatedAt_not_in?: any[...' is not assignable to type 'PostWhereInput | undefined'.
      Type 'null' is not assignable to type 'PostWhereInput | undefined'.

40       resolve: (_, args, { prisma }) => prisma.posts(args)
                                                        ~~~~


Found 1 error.

Ran into this issue from this example. I was gonna submit a PR to remove the comment, but I can't tell if this is resolved or not...

cc @divyenduz

We did fix this issue for the field but it is not fixed for the arguments. We still need to wrap arguments in the Maybe<T> type.

image

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sedubois picture sedubois  路  3Comments

schickling picture schickling  路  3Comments

jannone picture jannone  路  3Comments

notrab picture notrab  路  3Comments

marktani picture marktani  路  3Comments