Skip to content

lazy ​

在校验时才确定的 Schema。两种情况:

  • 递归 —— 包含自身的数据:带子分类的分类、带回复的评论、菜单。
  • 根据值或同级字段选择 Schema —— 例如机构的税号为 10 位且必须有 KPP,个人为 12 位且没有 KPP。

签名 ​

ts
function lazy<T>(getter: (value: unknown, info: { parent: unknown; root: unknown }) => Schema<T>): Schema<T>

参数 ​

参数类型说明
getter(value, { parent, root }) => Schema<T>返回 Schema 的函数。每次校验时调用,接收值、包含同级字段的对象以及全部数据。递归时不需要参数:() => Category

注意事项 ​

  • TypeScript 无法自行推导递归类型,因此需要声明类型并标注在 Schema 上:const Category: Schema<Category> = ...。
  • 防止过深的数据。 如果客户端发送数万层嵌套的数据,普通递归会因栈溢出导致进程崩溃。lazy 在达到 maxDepth(默认 256)时停止并返回 depth 错误 —— 服务端返回 422 而不是崩溃。可以通过 validate 的 maxDepth 选项修改限制。

错误代码 ​

代码触发条件默认信息
depth嵌套超过 maxDepthToo deeply nested (max {max})

示例 ​

分类树 ​

ts
import { object, string, array, optional, lazy, type Schema } from 'tinyval';

type Category = { name: string; children?: Category[] };

const Category: Schema<Category> = object({
  name: string(),
  children: optional(array(lazy(() => Category))),
});

validate(Category, { name: 'root', children: [{ name: 'a', children: [{}] }] }).errors;
// { 'children.0.children.0.name': 'Required' }

根据同级字段选择 Schema ​

ts
const Company = object({
  type: oneOf(['person', 'org']),
  inn: lazy((_, { parent }) => pipe(string(), length(parent.type === 'org' ? 10 : 12), inn())),
  kpp: lazy((_, { parent }) => (parent.type === 'org' ? pipe(string(), pattern(/^\d{4}[\dA-Z]{2}\d{3}$/)) : optional(string()))),
});

validate(Company, { type: 'org', inn: '500100732259' }).errors;
// { inn: 'Must be exactly 10 characters long', kpp: 'Required' }

根据值选择 Schema ​

ts
// id: a number or a UUID string
const id = lazy((value) => (typeof value === 'number' ? pipe(number(), integer()) : pipe(string(), uuid())));

为了 TypeScript 类型,所有分支应返回相同类型或联合类型的 Schema:结构不同的变体更适合用 variant 或 union。

带回复的评论 ​

ts
type Comment = { text: string; replies: Comment[] };

const Comment: Schema<Comment> = object({
  text: pipe(string(), trim(), minLength(1), maxLength(2000)),
  replies: optional(array(lazy(() => Comment)), () => []),
});

parse(Comment, body, { maxDepth: 10 }); // no more than 10 reply levels

另请参阅 ​