類型安全
Prisma Client 所產生的程式碼包含數種實用的型別與工具,您可以利用這些功能來提升應用程式的型別安全。本頁面將介紹如何運用這些功能的模式。
注意:如果您對 Prisma ORM 的進階型別安全主題感興趣,請務必查看這篇關於如何使用新的 TypeScript
satisfies關鍵字來改善 Prisma Client 工作流程的部落格文章。
匯入產生的型別
您可以匯入 Prisma 命名空間,並使用點記法(dot notation)來存取各種類型與工具。以下範例顯示如何匯入 Prisma 命名空間,並藉此存取與使用 Prisma.UserSelect 產生的型別。
import { Prisma } from '@prisma/client'
// Build 'select' object
const userEmail: Prisma.UserSelect = {
email: true,
}
// Use select object
const createUser = await prisma.user.create({
data: {
email: 'bob@prisma.io',
},
select: userEmail,
})
另請參閱:使用 Prisma.UserCreateInput 產生的型別
什麼是產生的型別?
產生的型別是從您的資料模型衍生而來的 TypeScript 型別。您可以使用這些型別來建立型別化的物件,並將其傳入頂層方法(例如 prisma.user.create(...) 或 prisma.user.update(...)),或是傳入諸如 select 或 include 等選項中。
例如,select 會接收一個 UserSelect 型別的物件。其物件屬性必須符合模型中 select 陳述式所支援的欄位。
下方的第一個分頁顯示了 UserSelect 產生的型別,以及該物件上的每個屬性如何具備型別註解。第二個分頁則顯示產出該型別的原始 Schema。
- 產生的型別
- 模型
type Prisma.UserSelect = {
id?: boolean | undefined;
email?: boolean | undefined;
name?: boolean | undefined;
posts?: boolean | Prisma.PostFindManyArgs | undefined;
profile?: boolean | Prisma.ProfileArgs | undefined;
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
}
在 TypeScript 中,型別註解 (type annotations) 是指在宣告變數時加入型別註解,以描述該變數的型別。請參閱下方範例。
const myAge: number = 37
const myName: string = 'Rich'
這兩個變數宣告都已加上型別註解,分別指定其原始型別為 number 和 string。大多數情況下,此類註解是不必要的,因為 TypeScript 會根據變數的初始值來推斷其型別。在上方的範例中,myAge 初始化為一個數字,因此 TypeScript 會推測其型別應為數字。
回到 UserSelect 型別,如果您在建立的 userEmail 物件上使用點記法,您將能夠存取 User 模型中所有可透過 select 陳述式進行互動的欄位。
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
}
import { Prisma } from '@prisma/client'
const userEmail: Prisma.UserSelect = {
email: true,
}
// properties available on the typed object
userEmail.id
userEmail.email
userEmail.name
userEmail.posts
userEmail.profile
同樣地,您也可以使用 include 產生的型別來定義物件,這樣您的物件就能存取那些可以使用 include 陳述式的屬性。
import { Prisma } from '@prisma/client'
const userPosts: Prisma.UserInclude = {
posts: true,
}
// properties available on the typed object
userPosts.posts
userPosts.profile
請參閱模型查詢選項 (model query options) 參考文件,以了解更多可用的型別資訊。
產生的 UncheckedInput 型別
UncheckedInput 型別是一組特殊的產生型別,允許您執行 Prisma Client 認為「不安全」的操作,例如直接寫入關聯純量欄位 (relation scalar fields)。在執行 create、update 或 upsert 等操作時,您可以選擇「安全」的 Input 型別或「不安全」的 UncheckedInput 型別。
例如,此 Prisma Schema 在 User 與 Post 之間定義了一對多關聯:
model Post {
id Int @id @default(autoincrement())
title String @db.VarChar(255)
content String?
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
第一個分頁顯示 PostUncheckedCreateInput 產生的型別。它包含 authorId 屬性,這是一個關聯純量欄位。第二個分頁顯示一個使用 PostUncheckedCreateInput 型別的範例查詢。如果 id 為 1 的使用者不存在,此查詢將會導致錯誤。
- 產生的型別
- 範例查詢
type PostUncheckedCreateInput = {
id?: number
title: string
content?: string | null
authorId: number
}
prisma.post.create({
data: {
title: 'First post',
content: 'Welcome to the first post in my blog...',
authorId: 1,
},
})
相同的查詢可以使用更「安全」的 PostCreateInput 型別來重寫。此型別不包含 authorId 欄位,而是改為包含 author 關聯欄位。
- 產生的型別
- 範例查詢
type PostCreateInput = {
title: string
content?: string | null
author: UserCreateNestedOneWithoutPostsInput
}
type UserCreateNestedOneWithoutPostsInput = {
create?: XOR<
UserCreateWithoutPostsInput,
UserUncheckedCreateWithoutPostsInput
>
connectOrCreate?: UserCreateOrConnectWithoutPostsInput
connect?: UserWhereUniqueInput
}
prisma.post.create({
data: {
title: 'First post',
content: 'Welcome to the first post in my blog...',
author: {
connect: {
id: 1,
},
},
},
})
如果 id 為 1 的作者不存在,此查詢同樣會導致錯誤。在這種情況下,Prisma Client 將會提供更具描述性的錯誤訊息。您也可以使用 connectOrCreate API,在給定 id 的使用者不存在時,安全地建立一個新使用者。
我們建議盡可能使用「安全」的 Input 型別。
型別工具
此功能適用於 Prisma ORM 4.9.0 及更高版本。
為了協助您建立高度型別安全的應用程式,Prisma Client 提供了一組能對接輸入與輸出型別的型別工具。這些型別是完全動態的,代表它們會適應任何給定的模型與 Schema。您可以使用它們來改善專案的自動完成功能與開發者體驗。
這在驗證輸入與共用的 Prisma Client 擴充功能中特別有用。
Prisma Client 提供以下型別工具:
Exact<Input, Shape>:對Input強制執行嚴格的型別安全。Exact確保泛型型別Input嚴格遵守您在Shape中指定的型別。它會將Input縮窄 (narrowing) 為最精確的型別。Args<Type, Operation>:擷取給定模型與操作的輸入引數。這對於想要執行以下動作的擴充功能作者特別有用:- 重複使用現有型別以進行擴充或修改。
- 享有與現有操作相同的自動完成體驗。
Result<Type, Arguments, Operation>:接收輸入引數並提供給定模型與操作的結果。通常會與Args搭配使用。如同Args,Result協助您重複使用現有型別來進行擴充或修改。Payload<Type, Operation>:擷取結果的完整結構,包含給定模型與操作的純量與關聯物件。例如,您可以使用此功能在型別層級判斷哪些鍵是純量或物件。
作為範例,以下是一個快速的方法,確保函數的引數與您將傳遞給 post.create 的內容相符:
type PostCreateBody = Prisma.Args<typeof prisma.post, 'create'>['data']
const addPost = async (postBody: PostCreateBody) => {
const post = await prisma.post.create({ data: postBody })
return post
}
await addPost(myData)
// ^ guaranteed to match the input of `post.create`