對模型型別的部分結構進行操作
在使用 Prisma Client 時,您的 Prisma Schema 中的每個模型都會被轉換為一個專用的 TypeScript 型別。例如,假設您有以下 User 和 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
}
從這個 schema 生成的 Prisma Client 程式碼包含了 User 型別的此表示形式:
export type User = {
id: string
email: string
name: string | null
}
問題:使用生成模型型別的變體
描述
在某些場景中,您可能需要生成的 User 型別的變體。例如,當您有一個函式期望一個包含 posts 關係的 User 模型例項時。或者當您需要在應用程式程式碼中僅傳遞 User 模型的 email 和 name 欄位的型別時。
解決方案
作為解決方案,您可以使用 Prisma Client 的輔助型別自定義生成的模型型別。
User 型別僅包含模型的標量欄位,但不包含任何關係。這是因為在 Prisma Client 查詢中,關係預設不包含。
然而,有時擁有一個包含關係的可用型別會很有用(即,您可以透過使用 include 的 API 呼叫獲得的型別)。同樣,另一個有用的場景是擁有一個只包含模型標量欄位的子集的可用型別(即,您可以透過使用 select 的 API 呼叫獲得的型別)。
實現這一點的一種方法是在應用程式程式碼中手動定義這些型別
// 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
}
雖然這確實可行,但這種方法會在 Prisma schema 更改時增加維護負擔,因為您需要手動維護這些型別。一個更簡潔的解決方案是結合使用 validator 和 Prisma Client 在 Prisma 名稱空間下生成並公開的 UserGetPayload 型別。
以下示例使用 Prisma.validator 建立了兩個型別安全的物件,然後使用 Prisma.UserGetPayload 實用函式建立了一個可用於返回所有使用者及其帖子的型別。
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>
後一種方法的主要優勢是:
- 更簡潔的方法,因為它利用了 Prisma Client 生成的型別
- 在 schema 更改時減少了維護負擔並提高了型別安全性
問題:獲取函式的返回型別
描述
當對模型執行 select 或 include 操作並從函式返回這些變體時,很難訪問到其返回型別,例如:
// Function definition that returns a partial structure
async function getUsersWithPosts() {
const users = await prisma.user.findMany({ include: { posts: true } })
return users
}
從上面的程式碼片段中提取代表“帶有帖子的使用者”的型別需要一些高階的 TypeScript 用法。
// 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()
解決方案
您可以優雅地使用原生的 TypeScript 實用型別 Awaited 和 ReturnType 來解決這個問題。
type UsersWithPosts = Awaited<ReturnType<typeof getUsersWithPosts>>