Zod 是一個專為 TypeScript 設計的強大模式驗證庫,它能夠無縫橋接編譯時類型系統與執行時環境,為開發者提供全面的類型安全保障。在現代 Web 應用程式開發中,尤其是處理使用者輸入、API 資料或 AI 整合時,Zod 已成為許多開發團隊不可或缺的工具。本文將深入探討 Zod 的核心特性以及使用它的主要優勢。
Zod 的核心概念
Zod 的設計理念是消除重複的類型宣告。在傳統的 TypeScript 開發中,我們經常需要先定義一個類型,然後再為該資料編寫驗證邏輯,這導致了重複的程式碼和潛在的不一致性。Zod 通過讓你定義一次驗證器,然後自動推斷出相應的 TypeScript 類型,解決了這個問題。
基本使用示例
import { z } from "zod";
// 創建一個基本的字串模式
const stringSchema = z.string();
// 解析和驗證
stringSchema.parse("hello"); // 成功返回 "hello"
try {
stringSchema.parse(12); // 抛出 ZodError
} catch (error) {
console.log((error as z.ZodError).errors);
/*
[
{
code: 'invalid_type',
expected: 'string',
received: 'number',
path: [],
message: 'Expected string, received number'
}
]
*/
}
// 安全解析 (不抛出錯誤)
const result = stringSchema.safeParse("hello");
if (result.success) {
console.log(result.data); // "hello"
} else {
console.log((result.error as z.ZodError).errors); // ZodError 實例
}
// 建立對象模式
const User = z.object({
username: z.string(),
email: z.string().email(),
age: z.number().min(18),
});
// 自動推斷類型
type User = z.infer<typeof User>;
// 等同於:{ username: string; email: string; age: number }
Zod 的主要優勢
1. 消除類型重複
Zod 最大的優勢之一是消除了在 TypeScript 中常見的類型重複問題。通過 z.infer
可以直接從 Zod 模式推斷出 TypeScript 類型,讓你的類型定義和驗證邏輯保持同步。
import { z } from "zod";
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(2),
email: z.string().email(),
role: z.enum(["admin", "user", "guest"]),
metadata: z.record(z.string()),
lastLogin: z.date().optional(),
});
// 自動推斷類型
type User = z.infer<typeof UserSchema>;
const user: User = {
id: "8dd7d69c-ab17-468c-80b5-c71658aa6b07",
name: "John Doe",
email: "john.doe@example.com",
role: "admin",
metadata: {
age: "30"
},
lastLogin: new Date("2023-03-15T12:34:56.789Z")
}
const result = UserSchema.parse(user);
console.log(result);
/*
{
id: '8dd7d69c-ab17-468c-80b5-c71658aa6b07',
name: 'John Doe',
email: 'john.doe@example.com',
role: 'admin',
metadata: { age: '30' },
lastLogin: 2023-03-15T12:34:56.789Z
}
*/
2. 執行時類型安全
TypeScript 僅提供編譯時的類型檢查,一旦編譯完成,類型安全就消失了。Zod 彌補了這一缺口,通過在執行時驗證資料結構,確保輸入確實符合預期的類型和格式。
// 表單提交處理
function handleSubmit(formData: unknown) {
const validatedData = UserSchema.parse(formData);
// 此處 validatedData 已通過驗證且類型安全
saveToDatabase(validatedData);
}
3. 零依賴與輕量級
Zod 完全沒有依賴其他套件,這使其成為一個非常輕量的解決方案。壓縮後僅約 8kb,遠小於許多同類庫。這對於前端應用尤為重要,因為最小化套件大小可以改善載入時間和使用者體驗。
4. 豐富的驗證功能
Zod 提供了豐富的內建驗證規則和方法,可以處理從簡單原始類型到複雜巢狀對象的各種資料結構。
import { z } from "zod";
// 字串驗證
const username = z.string()
.min(3, "用戶名至少需要 3 個字符")
.max(20, "用戶名不能超過 20 個字符");
// 數字驗證
const price = z.number()
.positive("價格必須為正數")
.min(0.01, "價格最少為 0.01");
// 複雜對象驗證
const Product = z.object({
id: z.string().uuid(),
name: z.string().min(3),
price: z.number().positive(),
tags: z.array(z.string()),
stock: z.number().int().nonnegative(),
});
const user = username.parse("calpa");
console.log(user);
const priceValue = price.parse(10.99);
console.log(priceValue);
const product = Product.parse({
id: "8dd7d69c-ab17-468c-80b5-c71658aa6b07",
name: "Product A",
price: 10.99,
tags: ["tag1", "tag2"],
stock: 10,
});
console.log(product);
你可以看到輸出結果,calpa
、10.99
和 product
都已經通過了驗證。
calpa
10.99
{
id: '8dd7d69c-ab17-468c-80b5-c71658aa6b07',
name: 'Product A',
price: 10.99,
tags: [ 'tag1', 'tag2' ],
stock: 10
}
5. 自定義驗證邏輯
通過 .refine()
和 .superRefine()
方法,Zod 允許你添加自定義的驗證邏輯,這在處理複雜業務規則時特別有用。
const passwordForm = z
.object({
password: z.string().min(8),
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "密碼不匹配",
path: ["confirmPassword"], // 指定錯誤路徑
});
6. 函數驗證能力
Zod 獨特的功能之一是能夠驗證函數的輸入和輸出。這是其他許多驗證庫沒有的特性。
const myFunction = z
.function()
.args(z.string(), z.number())
.returns(z.boolean())
.implement((str, num) => {
// TypeScript 知道 str 是字串,num 是數字
return str.length > num;
});
// 使用函數
myFunction("test", 3); // 返回 true
myFunction(123, "test"); // 類型錯誤
7. 與主流框架的良好整合
Zod 與 React、Next.js、tRPC 等現代框架有很好的整合。特別是在構建強類型的全端應用程式時,Zod 可以作為客戶端和服務器端之間的橋樑,確保資料一致性。
Zod 在 AI 開發中的應用
隨著 AI 應用程式的普及,Zod 已成為處理 AI 輸出的重要工具。從 OpenAI 到 Vercel 到 Google,各大 AI 平台的文檔中都出現了 Zod,用於確保 AI 生成的資料符合應用程式預期的結構。
// 定義 AI 輸出的結構
const AIResponseSchema = z.object({
text: z.string(),
confidence: z.number().min(0).max(1),
entities: z.array(
z.object({
type: z.enum(["person", "organization", "location"]),
text: z.string(),
score: z.number(),
})
),
});
// 處理 AI 回應
async function handleAIResponse(rawResponse: unknown) {
try {
const validResponse = AIResponseSchema.parse(rawResponse);
// 安全處理已驗證的資料
processEntities(validResponse.entities);
} catch (error) {
// 處理驗證錯誤
console.error("AI 回應格式無效", error);
}
}
Zod vs 其他驗證庫
與其他常見的驗證庫(如 Yup)相比,Zod 有幾個明顯的優勢:
-
TypeScript 優先:Zod 從設計之初就考慮到 TypeScript 整合,而 Yup 則是後來才添加 TypeScript 支援。
-
包大小更小:Zod 約 8KB,而 Yup 約 24KB。
-
更好的型別推斷:Zod 在類型推斷方面表現更好,可以捕捉到 Yup 可能忽略的類型錯誤。
-
不可變 API:Zod 的方法返回新實例而不是變更原對象,這與現代 JavaScript 的函數式編程理念更為一致。
結論
Zod 透過無縫橋接 TypeScript 的靜態類型系統和執行時驗證,為開發者提供了一個強大的工具,用於確保資料完整性和類型安全。它的輕量級設計、豐富的驗證功能和與 TypeScript 的深度整合,使其成為現代 Web 開發的理想選擇。
無論是處理表單輸入、API 回應,還是 AI 生成的資料,Zod 都能提供一致且可靠的驗證方案,同時消除重複的類型定義,提高開發效率。對於任何重視類型安全和程式碼品質的 TypeScript 項目,Zod 都是一個值得考慮的工具。
通過採用 Zod 進行資料驗證,你不僅可以提高程式碼的健壯性和可維護性,還能在開發過程中提早發現潛在問題,為用戶提供更好的體驗。