TypeScript 的新 satisfies
運算子允許一些新的、型別安全模式,這些模式以前需要冗長的型別註解或棘手的工作流程。本文涵蓋了幾個使用案例,說明它如何在常見的 Prisma 相關工作流程中幫助您。
目錄
一點背景知識
TypeScript 的優勢之一是它可以從上下文中推斷表達式的型別。例如,您可以宣告一個變數而無需型別註解,其型別將從您為其賦予的值中推斷出來。當值的確切型別複雜,並且明確註解型別將需要大量重複程式碼時,這尤其有用。
但是,有時明確的型別註解很有用。它們可以幫助向其他開發人員傳達您程式碼的意圖,並且它們可以使 TypeScript 錯誤盡可能接近錯誤的實際來源。
考慮一些定義訂閱定價層並使用 toFixed
方法將其轉換為字串的程式碼 Number
如果我們在 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 loader 函數。在 Remix loaders 之類的情況下,您也希望 TypeScript 推斷函數傳回的特定形狀。
在 TypeScript 4.9 之前,很難同時實現這兩者。使用 satisfies
運算子,我們現在可以確保函數與特殊函數簽名匹配,而不會擴展其傳回型別。
讓我們看一下一個使用 Remix loader 從 Prisma 傳回一些資料的範例
在這裡,satisfies
運算子執行三項操作
- 確保我們的
loader
函數與 Remix 中的LoaderFunction
簽名相容 - 從
LoaderFunction
簽名推斷我們函數的引數型別,因此我們不必手動註解它們 - 推斷我們的函數從 Prisma 傳回
Post
物件,包括其相關的comments
總結
TypeScript 和 Prisma 使在您的應用程式中獲得型別安全的資料庫存取變得容易。Prisma 的 API 旨在提供零成本型別安全,因此在大多數情況下,您可以自動獲得強型別檢查,而無需「選擇加入」、用型別註解弄亂您的程式碼或提供泛型引數。
我們很高興看到像 satisfies
運算子這樣的新 TypeScript 功能如何幫助您在更進階的情況下獲得更好的型別安全,並最大限度地減少型別雜訊。請透過我們的 Twitter 聯繫我們,讓我們知道您如何使用 Prisma 和 TypeScript 4.9。
不要錯過下一篇文章!
註冊 Prisma 電子報