$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:
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.