💎 Zod 4 is now stable!  Read the announcement.
Zod logo

元數據和註冊表 (Metadata and registries)

將架構與一些額外的 元數據 關聯通常很有用,用於文檔、代碼生成、AI 結構化輸出、表單驗證和其他目的。

註冊表 (Registries)

Zod 中的元數據通過 註冊表 處理。註冊表是架構的集合,每個架構都與一些 強類型 元數據相關聯。創建一個簡單的註冊表:

import * as z from "zod";
 
const myRegistry = z.registry<{ description: string }>();

要註冊、查找和從此註冊表中刪除架構:

const mySchema = z.string();
 
myRegistry.add(mySchema, { description: "A cool schema!"});
myRegistry.has(mySchema); // => true
myRegistry.get(mySchema); // => { description: "A cool schema!" }
myRegistry.remove(mySchema);
myRegistry.clear(); // wipe registry

TypeScript 強制每個架構的元數據與註冊表的 元數據類型 匹配。

myRegistry.add(mySchema, { description: "A cool schema!" }); // ✅
myRegistry.add(mySchema, { description: 123 }); // ❌

id 的特殊處理 — Zod 註冊表對 id 屬性進行特殊處理。如果多個架構註冊了相同的 id 值,將拋出 Error。這對所有註冊表(包括全局註冊表)都適用。

.register()

注意 — 此方法很特殊,因為它不返回新架構;相反,它返回原始架構。沒有其他 Zod 方法這樣做!這包括 .meta().describe()(如下文檔所述),它們返回一個新實例。

架構提供了一個 .register() 方法,以便更方便地將其添加到註冊表中。

const mySchema = z.string();
 
mySchema.register(myRegistry, { description: "A cool schema!" });
// => mySchema

這讓您可以在架構中「內聯」定義元數據。

const mySchema = z.object({
  name: z.string().register(myRegistry, { description: "The user's name" }),
  age: z.number().register(myRegistry, { description: "The user's age" }),
})

如果定義註冊表時沒有元數據類型,您可以將其用作通用的「集合」,不需要元數據。

const myRegistry = z.registry();
 
myRegistry.add(z.string());
myRegistry.add(z.number());

元數據 (Metadata)

z.globalRegistry

為方便起見,Zod 提供了一個全局註冊表 (z.globalRegistry),可用於存儲 JSON Schema 生成或其他目的的元數據。它接受以下元數據:

export interface GlobalMeta {
  id?: string ;
  title?: string ;
  description?: string;
  deprecated?: boolean;
  [k: string]: unknown;
}

要為架構在 z.globalRegistry 中註冊一些元數據:

import * as z from "zod";
 
const emailSchema = z.email().register(z.globalRegistry, { 
  id: "email_address",
  title: "Email address",
  description: "Your email address",
  examples: ["[email protected]"]
});

要全局擴展 GlobalMeta 接口,請使用 聲明合併。將以下內容添加到代碼庫中的任何位置。在項目根目錄中創建 zod.d.ts 文件是一種常見慣例。

declare module "zod" {
  interface GlobalMeta {
    // add new fields here
    examples?: unknown[];
  }
}
 
// forces TypeScript to consider the file a module
export {}

.meta()

為了更方便,請使用 .meta() 方法在 z.globalRegistry 中註冊架構。

const emailSchema = z.email().meta({ 
  id: "email_address",
  title: "Email address",
  description: "Please enter a valid email address",
});

在沒有參數的情況下調用 .meta()檢索 架構的元數據。

emailSchema.meta();
// => { id: "email_address", title: "Email address", ... }

元數據與 特定的架構實例 相關聯。牢記這一點很重要,特別是因為 Zod 方法是不可變的——它們總是返回一個新實例。

const A = z.string().meta({ description: "A cool string" });
A.meta(); // => { description: "A cool string" }
 
const B = A.refine(_ => true);
B.meta(); // => undefined

.describe()

.describe() 方法仍然存在是為了與 Zod 3 兼容,但 .meta() 現在是推薦的方法。

.describe() 方法是將架構註冊到 z.globalRegistry 並僅帶有 description 字段的簡寫。

const emailSchema = z.email();
emailSchema.describe("An email address");
 
// equivalent to
emailSchema.meta({ description: "An email address" });

自定義註冊表 (Custom registries)

您已經看到了一個簡單的自定義註冊表示例:

import * as z from "zod";
 
const myRegistry = z.registry<{ description: string };>();

讓我們看一些更高級的模式。

引用推斷類型 (Referencing inferred types)

元數據類型引用架構的 推斷類型 通常很有價值。例如,您可能希望 examples 字段包含架構輸出的示例。

import * as z from "zod";
 
type MyMeta = { examples: z.$output[] };
const myRegistry = z.registry<MyMeta>();
 
myRegistry.add(z.string(), { examples: ["hello", "world"] });
myRegistry.add(z.number(), { examples: [1, 2, 3] });

特殊符號 z.$output 是對架構推斷輸出類型的引用 (z.infer<typeof schema>)。同樣,您可以使用 z.$input 來引用輸入類型。

限制架構類型 (Constraining schema types)

將第二個泛型傳遞給 z.registry() 以限制可以添加到註冊表的架構類型。此註冊表僅接受字串架構。

import * as z from "zod";
 
const myRegistry = z.registry<{ description: string }, z.ZodString>();
 
myRegistry.add(z.string(), { description: "A number" }); // ✅
myRegistry.add(z.number(), { description: "A number" }); // ❌ 
//             ^ 'ZodNumber' is not assignable to parameter of type 'ZodString' 

On this page