💎 Zod 4 现已稳定发布! 阅读公告。
Zod logo

元数据与注册表

Edit this page

在文档、代码生成、AI结构化输出、表单验证及其他用途中,为某些架构关联一些额外的元数据常常非常有用。

注册表

Zod中的元数据通过注册表进行管理。注册表是架构的集合,每个架构都与某些强类型元数据关联。要创建一个简单的注册表:

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

要在此注册表中注册、查找和移除架构:

const mySchema = z.string();
 
myRegistry.add(mySchema, { description: "一个很棒的架构!" });
myRegistry.has(mySchema); // => true
myRegistry.get(mySchema); // => { description: "一个很棒的架构!" }
myRegistry.remove(mySchema);
myRegistry.clear(); // 清空注册表

TypeScript 会强制确保每个架构的元数据类型与注册表的元数据类型相匹配。

myRegistry.add(mySchema, { description: "一个很棒的架构!" }); // ✅
myRegistry.add(mySchema, { description: 123 }); // ❌

id 的特殊处理 — Zod 注册表对 id 属性进行特殊处理。如果注册了多个具有相同 id 值的模式,将会抛出 Error。这适用于所有注册表,包括全局注册表。

.register()

注意 — 该方法具有特殊性,它不会返回一个新的架构;而是返回原始的架构实例。没有其他 Zod 方法会这样!包括下面介绍的 .meta().describe()(它们会返回新的实例)。

架构定义方提供了 .register() 方法,以更方便地将其添加到注册表中。

const mySchema = z.string();
 
mySchema.register(myRegistry, { description: "一个很棒的架构!" });
// => mySchema

这允许你在架构定义中“内联”设置元数据。

const mySchema = z.object({
  name: z.string().register(myRegistry, { description: "用户的名字" }),
  age: z.number().register(myRegistry, { description: "用户的年龄" }),
});

如果没有定义元数据类型的注册表,你也可以将其作为一个通用的“集合”使用,不需要元数据。

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

元数据

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: "电子邮件地址",
  description: "您的电子邮件地址",
  examples: ["first.last@example.com"]
});

要全局扩展 GlobalMeta 接口,使用声明合并。在代码库中的任意位置添加以下内容。通常的约定是在项目根目录创建一个 zod.d.ts 文件。

declare module "zod" {
  interface GlobalMeta {
    // 在此添加新字段
    examples?: unknown[];
  }
}
 
// 防止 TypeScript 将该文件视为全局脚本文件
export {}

.meta()

为了更方便,使用 .meta() 方法在 z.globalRegistry 中注册架构的元数据。

const emailSchema = z.email().meta({ 
  id: "email_address",
  title: "电子邮件地址",
  description: "请输入有效的电子邮件地址",
});

调用 .meta() 而不带参数将获取架构的元数据。

emailSchema.meta();
// => { id: "email_address", title: "电子邮件地址", ... }

元数据与特定的架构实例相关联。这一点十分重要,尤其是因为 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("一个电子邮件地址");
 
// 等价于
emailSchema.meta({ description: "一个电子邮件地址" });

自定义注册表

你已经看过一个简单的自定义注册表示例:

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

下面我们来看一些更高级的用法。

引用推断的类型

将元数据类型引用为架构的推断类型通常非常有价值。例如,你可能希望 examples 字段包含该架构输出的示例。

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

特殊符号 z.$output 用于引用架构推断输出类型(等价于 z.infer<typeof schema>)。你也可以用 z.$input 来引用输入类型。

限制架构类型

传递第二个泛型参数给 z.registry(),以限制可以添加到注册表中的架构类型。此注册表仅接受字符串架构。

import * as z from "zod";
 
const myRegistry = z.registry<{ description: string }, z.ZodString>();
 
myRegistry.add(z.string(), { description: "一个字符串" }); // ✅
myRegistry.add(z.number(), { description: "一个数字" }); // ❌ 
//             ^ 'ZodNumber' 不能赋值给类型 'ZodString'

On this page