prisma / prisma-client-js

Type-safe database client for TypeScript & Node.js (ORM replacement)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

$transaction spread operator

TheAschr opened this issue · comments

Bug description

The $transaction function type definitions do not support spread operator. Typescript complains that it is not able to map the Alert type onto the Comment type.

How to reproduce

const [comment, ...unsentMessages] = await prisma.$transaction([
  prisma.comment.create({
    data: {},
  }),
  ...alerts.map((alert) =>
    prisma.unsentMessage.create({
      data: {
        content: alert.content,
      },
    })
  ),
]);

await Promise.all(
  unsentMessages.map(async (unsentMessage) => {
    await sendMessage(unsentMessage);
    await prisma.unsentMessage.delete({
      where: { id: unsentMessage.id },
    });
  })
);

Expected behavior

The $transaction function should allow passing in spread operator for transactional inserts on a variable length of unrelated items.

Environment & setup

  • Node.js version: 14.10.0
  • Typescript version: 3.9.7
  • Prisma version: @prisma/client@2.5.1

@TheAschr Sorry for the delay.

I am unable to reproduce this:
image

Can you please try this again in the latest version? If you can still reproduce, please share a minimal reproduction inside of a git repository.

No you got it wrong @pantharshit00, I got this problem too the other day.
The problem is when you use a spread iterator in the transaction not outside like with a map.
My solution was to manually force the type of transaction, so something like:

prisma.$transaction<MyType | MyOtherType>([
   ...myList.map(_ => {} as CreateMyTypeInput),
   ...myOtherList.map(_ => {} as CreateMyOtherTypeInput)
])

Thanks @Sytten! Closing as this is working as expected.

@matthewmueller I will take another look here just in case

Thanks @Sytten for clarification, I can reproduce with this simple example.

  const [user, ...otherUsers] = await prisma.$transaction([
    prisma.user.create({
      data: {
        email: "testsss",
      },
    }),
    ...[
      prisma.profile.create({
        data: {
          user: {
            connect: {
              id: 1,
            },
          },
        },
      }),
      prisma.profile.create({
        data: {
          user: {
            connect: {
              id: 2,
            },
          },
        },
      }),
    ],
  ]);
main.ts:6:59 - error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type '(Prisma__ProfileClient<Profile> | Prisma__UserClient<User>)[]' is not assignable to parameter of type 'Iterable<Profile | PromiseLike<Profile>>'.
      The types returned by '[Symbol.iterator]().next(...)' are incompatible between these types.
        Type 'IteratorResult<Prisma__ProfileClient<Profile> | Prisma__UserClient<User>, any>' is not assignable to type 'IteratorResult<Profile | PromiseLike<Profile>, any>'.
          Type 'IteratorYieldResult<Prisma__ProfileClient<Profile> | Prisma__UserClient<User>>' is not assignable to type 'IteratorResult<Profile | PromiseLike<Profile>, any>'.
            Type 'IteratorYieldResult<Prisma__ProfileClient<Profile> | Prisma__UserClient<User>>' is not assignable to type 'IteratorYieldResult<Profile | PromiseLike<Profile>>'.
              Type 'Prisma__ProfileClient<Profile> | Prisma__UserClient<User>' is not assignable to type 'Profile | PromiseLike<Profile>'.
                Type 'Prisma__UserClient<User>' is not assignable to type 'Profile | PromiseLike<Profile>'.
                  Type 'Prisma__UserClient<User>' is not assignable to type 'PromiseLike<Profile>'.
                    Types of property 'then' are incompatible.
                      Type '<TResult1 = User, TResult2 = never>(onfulfilled?: ((value: User) => TResult1 | Promise<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | Promise<...>) | null | undefined) => Promise<...>' is not assignable to type '<TResult1 = Profile, TResult2 = never>(onfulfilled?: ((value: Profile) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => PromiseLike<...>'.
                        Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
                          Types of parameters 'value' and 'value' are incompatible.
                            Type 'User' is missing the following properties from type 'Profile': bio, userId

  6   const [user, ...otherUsers] = await prisma.$transaction([
                                                              ~
  7     prisma.user.create({
    ~~~~~~~~~~~~~~~~~~~~~~~~
...
 31     ],
    ~~~~~~
 32   ]);
    ~~~

  node_modules/typescript/lib/lib.es2015.iterable.d.ts:226:5
    226     all<TAll>(values: Iterable<TAll | PromiseLike<TAll>>): Promise<TAll[]>;
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    The last overload is declared here.


Found 1 error.

Schema used:

model Post {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
}

model Profile {
  id     Int     @default(autoincrement()) @id
  bio    String?
  user   User    @relation(fields: [userId], references: [id])
  userId Int     @unique
}

model User {
  id      Int      @default(autoincrement()) @id
  email   String   @unique
  name    String?
  posts   Post[]
  profile Profile?
}

To clarify the expected behavior:

This is a bug. We shouldn't need to do the workaround described by @Sytten.

@pantharshit00 thinks it might be a consequence of our Promise-like interface but it will need further investigation.

This is not really a bug in the transaction types themselves. This points to a deeper problem in TypeScript. If you have two functions that return Promises of different types and try to combine them like this with the spread operator, the built-in Promise.all types break.

Corresponding TypeScript issue: microsoft/TypeScript#40330 (comment)

Unfortunately there's nothing we can do here for the time being. Closing.

@matthewmueller Since it is considered for TS 4.1.1 I would keep this issue open.