TypeScript 新的 `satisfies` 運算子允許使用一些以前需要冗長型別註解或巧妙變通的新型安全模式。本文將介紹它在哪些常見的 Prisma 相關工作流程中能為您提供幫助。
目錄
背景介紹
TypeScript 的優勢之一在於它能夠從上下文中 推斷 表示式的型別。例如,您可以宣告一個不帶型別註解的變數,其型別將從您為其賦的值中推斷出來。當值的確切型別很複雜,並且顯式註解型別會需要大量重複程式碼時,這尤其有用。
然而,有時顯式型別註解也很有用。它們可以幫助向其他開發者傳達程式碼的 意圖,並使 TypeScript 錯誤儘可能接近實際的錯誤來源。
考慮一些定義訂閱定價層並使用 `Number` 上的 `toFixed` 方法將其轉換為字串的程式碼
如果我們對 `plans` 使用顯式型別註解,我們可以更早地發現拼寫錯誤,並推斷 `users` 引數的型別。但是,我們可能會遇到另一個問題
當我們使用顯式型別註解時,型別會“拓寬”,TypeScript 無法再區分我們的哪些計劃是固定價格,哪些是按使用者定價的。實際上,我們“丟失”了關於應用程式型別的一些資訊。
我們真正需要的是一種方法,可以斷言一個值與某個寬泛/可重用的型別相容,同時讓 TypeScript 推斷出更窄(更具體)的型別。
受約束的恆等函式
在 TypeScript 4.9 之前,解決這個問題的一種方法是使用一種 “受約束的恆等函式”。這是一種泛型、無操作的函式,它接受一個引數和一個型別引數,以確保兩者相容。
這種函式的一個例子是 Prisma.validator 工具,它還會做一些額外的工作,以僅允許在提供的泛型型別中定義的已知欄位。
不幸的是,這個解決方案會產生一些執行時開銷,僅僅是為了在編譯時讓 TypeScript 滿意。一定有更好的方法!
`satisfies` 簡介
新的 `satisfies` 運算子提供了相同的優勢,沒有執行時影響,並且自動檢查多餘或拼寫錯誤的屬性。
讓我們看看我們的定價層示例在 TypeScript 4.9 中會是什麼樣子
現在我們可以在源頭捕獲拼寫錯誤,但不會因為型別拓寬而“丟失”任何資訊。
本文的其餘部分將介紹一些您在 Prisma 應用程式中可能使用 `satisfies` 的實際情況。
無需 `Prisma.validator` 即可推斷 Prisma 輸出型別
Prisma Client 使用泛型函式為您提供型別安全的結果。客戶端方法返回的資料的靜態型別與您在查詢中請求的形狀匹配。
當直接使用內聯引數呼叫 Prisma 方法時,這種方式效果很好
但是,您可能會遇到一些陷阱
- 如果您嘗試將查詢引數分解成更小的物件,型別資訊可能會“丟失”(拓寬),Prisma 可能無法正確推斷輸出型別。
- 很難獲得表示特定查詢輸出的型別。
`satisfies` 運算子可以提供幫助。
推斷 `findMany` 和 `create` 等方法的輸出型別
`satisfies` 運算子與 Prisma 最常見的用例之一是推斷特定查詢方法(如 `findUnique`)的返回型別——包括模型及其關係中僅選擇的欄位。
推斷 `count` 方法的輸出型別
Prisma Client 的 `count` 方法允許您新增一個 `select` 欄位,以便計算指定欄位非空值的行數。此方法的返回型別取決於您指定的欄位
推斷 `aggregate` 方法的輸出型別
我們還可以獲取更靈活的 `aggregate` 方法的輸出形狀,該方法允許我們獲取各種模型欄位的平均值、最小值、最大值和計數。
推斷 `groupBy` 方法的輸出型別
`groupBy` 方法允許您對模型例項組執行聚合。結果將包括用於分組的欄位,以及聚合欄位的結果。以下是您如何使用 `satisfies` 推斷輸出型別的方法
建立無損模式驗證器
模式驗證庫(例如 zod 或 superstruct)是執行時清理使用者輸入的不錯選擇。其中一些庫可以透過推斷模式的靜態型別來幫助您減少重複的型別定義。但是,有時您可能希望為現有 TypeScript 型別(例如 Prisma 生成的輸入型別)建立模式驗證器。
例如,在您的 Prisma 模式檔案中給出像這樣的 `Post` 型別
Prisma 將生成以下 `PostCreateInput` 型別
如果您嘗試使用 zod 建立與此型別匹配的模式,您將“丟失”有關模式物件的一些資訊
在 TypeScript 4.9 之前,一種解決方法是建立一個 schemaForType 函式(一種受約束的恆等函式)。現在,使用 `satisfies` 運算子,您可以為現有型別建立模式,而不會丟失任何有關模式的資訊。
以下是四種流行模式驗證庫的一些示例
定義可重用的查詢過濾器集合
隨著應用程式的增長,您可能會在許多查詢中使用相同的過濾邏輯。您可能希望定義一些可以重用並組合成更復雜查詢的通用過濾器。
一些 ORM 具有內建的方法來做到這一點——例如,您可以在 Ruby on Rails 中定義模型作用域,或者在 Django 中建立自定義查詢集方法。
使用 Prisma,`where` 條件是物件字面量,可以與 `AND`、`OR` 和 `NOT` 組合。`satisfies` 運算子為我們提供了一種方便的方法來定義可重用的過濾器集合。
具有推斷返回型別的強型別函式
有時您可能希望斷言一個函式匹配特定的函式簽名,例如 React 元件或 Remix 載入器函式。在 Remix 載入器等情況下,您還希望 TypeScript 推斷函式返回的具體形狀。
在 TypeScript 4.9 之前,很難同時實現這兩點。有了 `satisfies` 運算子,我們現在可以確保函式匹配特定的函式簽名,而不會拓寬其返回型別。
讓我們看一個 Remix 載入器從 Prisma 返回一些資料的例子
這裡 `satisfies` 運算子做了三件事
- 確保我們的 `loader` 函式與 Remix 的 `LoaderFunction` 簽名相容
- 從 `LoaderFunction` 簽名推斷我們函式的引數型別,這樣我們就不必手動註解它們
- 推斷我們的函式返回一個 Prisma 的 `Post` 物件,包括其相關的 `comments`
總結
TypeScript 和 Prisma 讓在您的應用程式中獲得型別安全的資料庫訪問變得容易。Prisma 的 API 旨在提供零成本型別安全,因此在大多數情況下,您會自動獲得強大的型別檢查,而無需“選擇加入”、用型別註解混淆程式碼或提供泛型引數。
我們很高興看到 `satisfies` 運算子等新的 TypeScript 功能如何幫助您獲得更好的型別安全,即使在更高階的情況下也能最大程度地減少型別干擾。請在我們的Twitter上告訴我們您如何使用 Prisma 和 TypeScript 4.9。
不要錯過下一篇文章!
訂閱 Prisma 新聞通訊