結合 Zod 與 TypeScript,為 AI 應用建立可靠的資料驗證機制

作者: Calpa Liu
字數:3505
出版:2025 年 4 月 1 日
分類: TypeScript 開發工具 前端開發 資料驗證 AI
結合 TypeScript 與 Zod,為 AI 專案打造類型安全與執行時驗證兼具的開發體驗。本文示範如何在 Vibe Coding 工作流中強化資料可信度與錯誤處理。

在專案開發初期,我原本只是單純想透過 AI 工具提升程式撰寫效率,沒想到很快就遇到資料格式與型別混亂的問題。模型生成的結果,外觀看似正確,卻時常潛藏型別錯誤或結構異常,輕則導致前端 bug,重則讓整個應用崩潰。這讓我深刻體會到,僅靠 TypeScript 的靜態型別檢查遠遠不夠,真正可靠的資料驗證必須貫徹到執行時階段。

Zod 正好補足了這一環。它專為 TypeScript 設計,讓你用宣告式方式一次定義驗證規則和型別推斷,省去繁瑣的重複定義。從 AI 輸出、API 資料到表單輸入,Zod 都能即時攔截錯誤資料,讓資料流更安全、開發更安心。

這篇文章是我將 Zod 實際應用於 AI 工具開發、API 串接與前端資料處理的經驗總結。不論你是要提升資料可信度,還是追求開發效率與型別安全,這裡都能幫你避開坑洞、打造更穩健的 TypeScript 專案。

Zod 是什麼?為什麼適合 AI 應用?

Zod 的設計理念是消除重複的類型宣告。在傳統的 TypeScript 開發中,我們經常需要先定義一個類型,然後再為該資料編寫驗證邏輯,這導致了重複的程式碼和潛在的不一致性。Zod 通過讓你定義一次驗證器,然後自動推斷出相應的 TypeScript 類型,解決了這個問題。

與其他驗證庫(如 Yup)相比,Zod 在多個層面展現出明顯優勢。首先,Zod 自設計之初即高度整合 TypeScript,能實現更佳的型別推斷,減少潛在錯誤,這是 Yup 等庫後期才逐步補強的特性。此外,Zod 的包體積僅約 8KB,遠小於 Yup 的 24KB,對於前端專案尤為適合,能有效優化載入體驗。

其次,Zod 採用不可變 API,所有方法都返回新實例,不會修改原始對象,這更符合現代 JavaScript 的函數式編程理念。這些設計讓 Zod 不僅在 TypeScript 專案中佔有優勢,也成為追求簡潔、高效、型別安全開發者的首選。

如何安裝 Zod

安裝 Zod 很簡單,只需要執行以下命令即可:

npm install zod

或者使用 yarn:

yarn add zod

這樣你就可以開始使用 Zod 了。

Zod 基本用法與資料驗證範例

Zod 是一個為 TypeScript 設計的宣告式模式驗證工具。它能同時進行「執行時資料驗證」與「型別推斷」,讓你只需定義一次資料結構,就能自動獲得型別安全與驗證能力。這對於處理外部 API 資料、AI 輸出或表單輸入等不可信來源,特別重要。以下範例展示 Zod 的基本用法:

import { z } from "zod";

// 建立字串模式
const stringSchema = z.string();

// 驗證成功
stringSchema.parse("hello"); // => "hello"

// 驗證失敗(將拋出 ZodError)
try {
  stringSchema.parse(12);
} 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 details
}

// 定義物件模式
const UserSchema = z.object({
  username: z.string(),
  email: z.string().email(),
  age: z.number().min(18),
});

// 型別自動推斷
type User = z.infer<typeof UserSchema>;
// 等同於:{ username: string; email: string; age: number }

在實務開發中,僅僅依靠 TypeScript 靜態型別檢查,往往無法保障資料於執行時的完整性與正確性。Zod 則彌補了這一缺口,讓型別定義與驗證規則合而為一,減少重複維護負擔,同時強化每一道資料流的安全防線。這種 schema 驅動的驗證模式,不僅讓你更安心接收外部 API 或 AI 輸出,也大幅優化前後端協作效率,為現代 AI 應用建立穩健的資料基石。

一次定義,推斷類型:告別重複維護

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
}
*/

這不僅減少了維護負擔,也讓類型錯誤更早在開發階段被發現,是大幅提升穩定性的第一步。

編譯與執行雙保險,強化型別安全

TypeScript 僅提供編譯時的類型檢查,一旦編譯完成,類型安全就消失了。Zod 彌補了這一缺口,通過在執行時驗證資料結構,確保輸入確實符合預期的類型和格式。

// 表單提交處理器:結合 TypeScript 類型與 Zod 執行時驗證
function handleSubmit(formData: unknown) {
  try {
    // 驗證資料結構與型別
    const validatedData = UserSchema.parse(formData);
    // 通過驗證後的資料具備完整型別安全
    saveToDatabase(validatedData);
    console.log("資料已成功儲存");
  } catch (error) {
    if (error instanceof z.ZodError) {
      // 輸出詳細驗證錯誤訊息
      console.error("表單驗證失敗:", error.errors);
    } else {
      console.error("未知錯誤:", error);
    }
  }
}

結合靜態與動態驗證,讓應用層層把關,提升穩定性與安全性。

沒有依賴,體積小巧,適合前端部署

Zod 完全沒有依賴其他套件,這使它成為一個極為輕量的解決方案。經過壓縮後,Zod 的體積僅約 8KB,遠小於如 Yup(約 24KB)或 Joi 等同類型驗證庫。這種輕量特性對於需要優化資源載入速度的前端應用尤其重要,能有效縮短頁面初次渲染時間,提升用戶體驗。

此外,Zod 的零依賴設計意味著它不會因外部套件變動而引入額外風險,維護起來更加穩定可靠。這也讓它非常適合在 Serverless、Edge Function、瀏覽器端等對體積和穩定性有嚴格要求的場景下使用。即使在大型單頁應用(SPA)或移動端 PWA 專案中,Zod 也能輕鬆融入,而不會拖慢整體效能。

與此同時,Zod 仍然保有強大且完整的驗證能力,無論是複雜的巢狀物件、陣列、聯合型別,還是用 refine 與 superRefine 實現自定義規則都能輕鬆處理。這讓開發者不必在效能與功能間妥協,能放心地將 Zod 用於要求嚴格的前端與 AI 應用。

豊富的驗證功能

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);

你可以看到輸出結果,calpa10.99product 都已經通過了驗證。

calpa
10.99
{
  id: '8dd7d69c-ab17-468c-80b5-c71658aa6b07',
  name: 'Product A',
  price: 10.99,
  tags: [ 'tag1', 'tag2' ],
  stock: 10
}

用 refine 與 superRefine 實現自定義規則

通過 .refine().superRefine() 方法,Zod 允許你添加自定義的驗證邏輯,這在處理複雜業務規則時特別有用。

const passwordForm = z
  .object({
    password: z.string().min(8),
    confirmPassword: z.string(),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: "密碼不匹配",
    path: ["confirmPassword"], // 指定錯誤路徑
  });

驗證函數參數與回傳值,提升邏輯安全性

Zod 的一大亮點是可以直接驗證函數的參數與回傳值型別,確保每一次調用都符合預期結構,讓商業邏輯更嚴謹,減少潛在錯誤。

基本用法

import { z } from "zod";

// 定義一個函數,參數型別為 string 和 number,回傳 boolean
const isLongerThan = z
  .function()
  .args(z.string(), z.number())
  .returns(z.boolean())
  .implement((text, minLength) => text.length > minLength);

// 正確用法
console.log(isLongerThan("hello", 3)); // true

// 錯誤用法將在型別檢查階段被攔截
// isLongerThan(123, "hi"); // ❌ TypeScript 編譯不通過

Zod 讓你像寫型別一樣寫驗證,將型別安全提升到運行階段,讓業務邏輯更可靠!

進階:複雜參數與回傳型別

你可以驗證更複雜的參數結構,例如物件、陣列,甚至多個參數:

const sumNumbers = z
  .function()
  .args(z.array(z.number()))
  .returns(z.number())
  .implement((nums) => nums.reduce((a, b) => a + b, 0));

// 正確用法
console.log(sumNumbers([1, 2, 3])); // 6

// 錯誤用法(型別不符)
// sumNumbers("123"); // ❌

用於 API 路由與業務邏輯

這種函數型驗證方式非常適合應用於 API 路由、服務層,或任何要求「輸入/輸出型別安全」的場景。例如:

const registerUser = z
  .function()
  .args(
    z.object({
      username: z.string().min(3),
      password: z.string().min(8),
    })
  )
  .returns(z.object({ id: z.string().uuid() }))
  .implement(async ({ username, password }) => {
    // 處理註冊邏輯...
    return { id: crypto.randomUUID() };
  });

// 呼叫時自動檢查型別
registerUser({ username: "calpa", password: "supersecret123" }).then(console.log);
// registerUser({ username: "ab", password: "123" }); // ❌ 參數驗證失敗

與 React、tRPC 等框架無縫整合

Zod 可完美整合於現代前後端框架,例如 React 與 tRPC。它讓你不僅能在前端開發中驗證表單資料、API 請求和回應,還能將型別安全從編譯時延伸到執行時。這種端到端的驗證,大幅提升資料正確性與應用穩定性,有效防止錯誤數據流入,顯著降低潛在風險。

React 結合 Zod 驗證表單資料範例

你可以使用 Zod 來驗證 React 表單資料,以下是一個簡單的範例:

import React, { useState } from "react";
import { z } from "zod";

const schema = z.object({
  email: z.string().email({ message: "請輸入有效的電子郵件" }),
  age: z
    .number({ invalid_type_error: "年齡必須為數字" })
    .min(18, { message: "必須年滿 18 歲" }),
});

export default function SignupForm() {
  const [form, setForm] = useState({ email: "", age: "" });
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState(false);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setForm({ ...form, [e.target.name]: e.target.value });
    setError(null);
    setSuccess(false);
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const parseResult = schema.safeParse({
      email: form.email,
      age: Number(form.age),
    });
    if (!parseResult.success) {
      setError(parseResult.error.errors.map(e => e.message).join(","));
      setSuccess(false);
      return;
    }
    setError(null);
    setSuccess(true);
    // 實際應用場景可在此處發送 API 請求
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="email"
        value={form.email}
        onChange={handleChange}
        placeholder="Email"
        type="email"
        required
      />
      <input
        name="age"
        value={form.age}
        onChange={handleChange}
        placeholder="年齡"
        type="number"
        required
      />
      <button type="submit">提交</button>
      {error && <div style={{ color: "red" }}>{error}</div>}
      {success && <div style={{ color: "green" }}>提交成功!</div>}
    </form>
  );
}

Zod 不僅能夠提升型別安全,更讓你在 React、tRPC 等現代框架中,實現「編譯時 + 執行時」雙重保護。這種即時驗證的能力,大幅降低了資料錯誤進入業務邏輯層的風險。

不論是 API 輸入、前端表單還是複雜嵌套物件,Zod 都能以極簡的語法完成嚴謹的驗證,讓你的 TypeScript 專案更安全、更可靠。

tRPC 結合 Zod 驗證 API 輸入輸出範例

import { z } from "zod";
import { createRouter } from "@trpc/server";

const schema = z.object({
  email: z.string().email(),
  age: z.number().min(18),
});

const router = createRouter()
  .mutation("signup", {
    input: schema,
    resolve: ({ input }) => {
      // 處理通過驗證的資料
      alert("提交成功!");
    },
  });

對於 tRPC 這類強調全端型別安全的方案,Zod 扮演著「溝通橋樑」的角色。你可以在伺服器端定義好 Zod schema,然後直接推導出 TypeScript 類型,讓前端呼叫 API 時自動擁有正確型別提示,徹底消除型別不一致的隱患。

這種整合方式不僅減少了重複維護型別和驗證邏輯的麻煩,也讓開發流程更自動化、更可靠。無論是處理用戶輸入、API 輸出還是 AI 模型回傳的原始資料,Zod 都能幫你快速攔截錯誤並給出明確回饋,讓你的應用程式在資料層面更加健壯。

在 AI 專案中導入 Zod 的關鍵場景與實例

隨著 AI 應用程式的普及,Zod 已成為處理 AI 輸出的重要工具。從 OpenAI、Vercel 到 Google,越來越多的 AI 平台文檔中推薦 Zod 來確保 AI 生成資料符合應用預期結構。這種驗證不僅提升穩定性,也大幅減少資料錯誤導致的 bug 與安全隱患。

舉例來說,假設你要接收一個 LLM 回傳的內容,並對其中的實體進行後續運算:

import { z } from "zod";

// 定義 AI 回傳資料的 schema
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().min(0).max(1),
    })
  ),
});

// 處理 AI 回應
async function handleAIResponse(rawResponse: unknown) {
  const result = AIResponseSchema.safeParse(rawResponse);
  if (!result.success) {
    console.error("AI 回應格式無效", result.error.issues);
    return;
  }
  // result.data 已自動型別推導且安全
  processEntities(result.data.entities);
}

// 假設的實體處理函數
function processEntities(entities: Array<{ type: string; text: string; score: number }>) {
  entities.forEach((entity) => {
    console.log(`[${entity.type}] ${entity.text} (score: ${entity.score})`);
  });
}

這種寫法讓你不必擔心 AI 回傳結構突變或資料異常,所有型別檢查與數值範圍都在執行時由 Zod 把關。AI 開發中,這不但減少 debug 時間,更能大幅提升生產環境的穩定性。

結論

Zod 就像是 TypeScript 的資料守門員,讓你不再擔心資料結構與實際輸入脫節的問題。它橋接了開發者最常面對的兩端——靜態型別與動態資料,無論你面對的是表單、API、還是 AI 模型,都能幫你築起一道清晰又安全的資料牆。

對我來說,這不只是提升開發效率,更是讓 Vibe Coding 真正穩定運作的基礎。如果你也在追求高可靠性的 AI 專案工作流,Zod 絕對值得成為你的資料驗證首選。

TypeScript 開發工具 前端開發 資料驗證 AI
關於 Calpa

Calpa 擅長使用 TypeScriptReact.jsVue.js 建立 Responsive Website。

他積極參與開源社區,曾在 2019 年的香港開源大會上擔任講者,提供工作經驗和見解。此外,他也在 GitHub 上公開分享個人博客程式碼,已獲得超過 300 顆星星和 60 個分支的支持。

他熱愛學習新技術,並樂意分享經驗。他相信,唯有不斷學習才能跟上快速演變的技術環境。

熱門文章

最新文章

圖片管理中心
管理圖片資源
IP 查詢
快速查詢和定位 IP 地址的地理位置和相關信息
Python 運行器
無需後端、無需登入,只需打開瀏覽器即可運行 Python 代碼(由 Pyodide 提供支持)
封面圖生成器
自動創建適合各種平台的文章封面圖
原作(青山剛昌)產生器
一鍵創建原作(青山剛昌)的封面圖
日本色彩
探索和使用傳統日本色彩
部落格內容洞察儀表板
以視覺化儀表板方式追蹤文章成效、分享熱度與分類分布,協助創作者掌握內容表現。
蒙特卡羅估算 π
使用蒙特卡羅方法演示 π 值的估算過程
LLM
使用 LLM 模型進行聊天
活動圖生成器
一鍵創建活動的封面圖
Wagmi Card
一鍵創建 Wagmi 的封面圖
Facebook Quote
Facebook Quote
Music Macro Language (MML) Studio
用程式語法編寫旋律,用音符構築想像
Blurhash
一鍵創建 Blurhash
文字分類器
使用 MediaPipe TextClassifier 分類文字
前端工程師免費工具資源
前端工程師免費工具資源
後端工程師免費工具資源
後端工程師免費工具資源
全端工程師免費工具資源
全端工程師免費工具資源
Web3 工程師免費工具資源
Web3 工程師免費工具資源
紫微斗數排盤系統|結合 AI 的命盤性格與事業財務分析生成器
紫微斗數排盤工具,輸入生日與時辰,自動生成完整命盤分析提示(Prompt)。結合最專業紫微理論與 AI 助力,助你深入解析性格、事業、財務與人際課題。免費使用,適合命理師及紫微愛好者。