-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Use @map'ed enum values as TS enum values #8446
Copy link
Copy link
Closed
Labels
kind/featureA request for a new feature.A request for a new feature.tech/typescriptIssue for tech TypeScript.Issue for tech TypeScript.topic: @maptopic: breaking changetopic: enum"type"/block `enum`"type"/block `enum`
Milestone
Description
Problem
Consider the following schema:
model Payment {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
amount Decimal @db.Decimal(14, 2)
provider PaymentProvider
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz
@@index([provider])
@@map("payments")
}
enum PaymentProvider {
MixplatSMS @map("mixplat/sms")
InternalToken @map("internal/token")
Offline @map("offline")
@@map("payment_provider")
}In database, it works as expected, the enum name and values are taken from the @map and @@map directives. In TypeScript, however, the generated values are as follows:
export const PaymentProvider: {
MixplatSMS: 'MixplatSMS',
InternalToken: 'InternalToken',
Offline: 'Offline'
};
export type PaymentProvider = (typeof PaymentProvider)[keyof typeof PaymentProvider]Which is a problem, because every time you want to serialize the object returned from the database by prisma, you have to remap these values to the ones that the project uses (which is the database version). A simplified example would look like this:
import { Middleware } from 'koa'
import { PaymentProvider } from '@prisma/client'
export let mapper: Record<PaymentProvider, string> = {
[PaymentProvider.MixplatSMS]: 'mixplat/sms',
[PaymentProvider.InternalToken]: 'internal/token',
[PaymentProvider.Offline]: 'offline',
}
export let reverseMapper: Record<string, PaymentProvider> = {
'mixplat/sms': PaymentProvider.MixplatSMS,
'internal/token': PaymentProvider.InternalToken,
'offline': PaymentProvider.Offline,
}
export let createPaymentHandler: Middleware = async (ctx, next) => {
let payment = await prisma.payment.create({
data: {
...ctx.request.body,
// Here we remap the value of provider from the project's version to the
// one prisma understands (e.g. from 'mixplat/sms' to 'MixplatSMS').
provider: reverseMapper[ctx.request.body.provider]
}
})
// ...
}
export let getPaymentHandler: Middleware = async (ctx, next) => {
let payment = await prisma.payment.findFirst()
ctx.body = {
...payment,
// Here we remap the prisma's value of provider to the one that the project
// actually uses (e.g. from 'InternalToken' to 'internal/token').
provider: mapper[payment.provider]
}
}It gets much worse when there are nested includes.
Suggested solution
If explicitly defined, use values from @map directives as enum values, like so:
export const PaymentProvider: {
MixplatSMS: 'mixplat/sms'
InternalToken: 'internal/token'
Offline: 'offline'
}
export type PaymentProvider = (typeof PaymentProvider)[keyof typeof PaymentProvider]Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
kind/featureA request for a new feature.A request for a new feature.tech/typescriptIssue for tech TypeScript.Issue for tech TypeScript.topic: @maptopic: breaking changetopic: enum"type"/block `enum`"type"/block `enum`