看完昨天的 prisma validator 的功能後,大家對 type safe 是不是又更了解了呢?今天補充一些情境讓你不得不用 prisma validator
以下是今天的 demo 會用到的 model,簡單加上一個 User 的 model 然後用一對多的關聯式去 relation Post
model User {
  id    Int     @id
  email String  @unique
  name  String?
  posts Post[]
}
model Post {
  id        Int     @id
  author    User    @relation(fields: [userId], references: [id])
  title     String
  published Boolean @default(false)
  userId    Int
}
每當我們 migrate DB 後 prisma client 會自動產生以下的 type 這邊來說就是 User
export declare type User = {
  id: string
  email: string
  name: string | null
}
但這時我們可能會遇到一個情境會是,假設你有一個 function 然後需要定義 User return 的 type,但仔細看一下 User type 他並沒有任何關於 post 的內容 ,原因就是 prisma 預設不會 return relation 的 data ,同時也不會產生關於 post 的型別,所以這時候我們只能自己 customize 我們要的 User type 同時包含 post 的內容
所以你會去定義如以下的 UserWithPosts 以及 UserPersonalData 來區別 return 的 data 是否有 post
// 1: Define a type that includes the relation to `Post`
type UserWithPosts = {
  id: string
  email: string
  name: string | null
  posts: Post[]
}
// 2: Define a type that only contains a subset of the scalar fields
type UserPersonalData = {
  email: string
  name: string | null
}
以上的寫法雖然很有彈性,但其實對於日後維護會添加很多成本,原因是每當你重新 generate prisma client 都需要手動去 update UserWithPosts 跟 UserPersonalData 的型別,但其實你不用這麼麻煩,prisma 有提供 utils function 自動給你你要的 type
import { Prisma } from '@prisma/client'
// 1: Define a type that includes the relation to `Post`
const userWithPosts = Prisma.validator<Prisma.UserDefaultArgs>()({
  include: { posts: true },
})
// 2: Define a type that only contains a subset of the scalar fields
const userPersonalData = Prisma.validator<Prisma.UserDefaultArgs>()({
  select: { email: true, name: true },
})
// 3: This type will include a user and all their posts
type UserWithPosts = Prisma.UserGetPayload<typeof userWithPosts>
以上的寫法可以讓整體的 code 更乾淨,同時對於日後維護也更方便
另外一個情境會是當我們使用 include 或是 select 的時候我們很難拿到 getUsersWithPosts 的 return type
async function getUsersWithPosts() {
  const users = await prisma.user.findMany({ include: { posts: true } })
  return users
}
所以常見的情況會是,我們要訂很多的 type 去拿到 getUsersWithPosts return 的型別
// Function definition that returns a partial structure
async function getUsersWithPosts() {
  const users = await prisma.user.findMany({ include: { posts: true } })
  return users
}
// Extract `UsersWithPosts` type with
type ThenArg<T> = T extends PromiseLike<infer U> ? U : T
type UsersWithPosts = ThenArg<ReturnType<typeof getUsersWithPosts>>
// run inside `async` function
const usersWithPosts: UsersWithPosts = await getUsersWithPosts()
但其實我們可以直接用 Prisma.PromiseReturnType 就可以簡單拿到我們要的 type 了
import { Prisma } from '@prisma/client'
type UsersWithPosts = Prisma.PromiseReturnType<typeof getUsersWithPosts>
✅ 前端社群 :
https://lihi3.cc/kBe0Y