custom-format-validators

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Custom Format Validators in z-schema

z-schema中的自定义格式验证器

JSON Schema
format
constrains string (or other) values beyond basic type checking. z-schema includes built-in validators and supports registering custom ones — both sync and async.
JSON Schema的
format
关键字在基础类型检查之外,对字符串(或其他类型)的值进行约束。z-schema包含内置验证器,同时支持注册自定义验证器——包括同步和异步两种类型。

Built-in formats

内置格式

z-schema ships with validators for all standard JSON Schema formats:
FormatValidates
date
RFC 3339 full-date (
2024-01-15
)
date-time
RFC 3339 date-time (
2024-01-15T09:30:00Z
)
time
RFC 3339 time (
09:30:00Z
)
email
RFC 5321 email address
idn-email
Internationalized email
hostname
RFC 1123 hostname
idn-hostname
Internationalized hostname
ipv4
IPv4 address
ipv6
IPv6 address
uri
RFC 3986 URI
uri-reference
URI or relative reference
uri-template
RFC 6570 URI template
iri
Internationalized URI
iri-reference
Internationalized URI reference
json-pointer
RFC 6901 JSON Pointer
relative-json-pointer
Relative JSON Pointer
regex
ECMA-262 regex
duration
ISO 8601 duration
uuid
RFC 4122 UUID
z-schema内置了所有标准JSON Schema格式的验证器:
格式名称验证规则
date
RFC 3339标准的完整日期(如
2024-01-15
date-time
RFC 3339标准的日期时间(如
2024-01-15T09:30:00Z
time
RFC 3339标准的时间(如
09:30:00Z
email
RFC 5321标准的邮箱地址
idn-email
国际化邮箱地址
hostname
RFC 1123标准的主机名
idn-hostname
国际化主机名
ipv4
IPv4地址
ipv6
IPv6地址
uri
RFC 3986标准的URI
uri-reference
URI或相对引用
uri-template
RFC 6570标准的URI模板
iri
国际化URI
iri-reference
国际化URI引用
json-pointer
RFC 6901标准的JSON指针
relative-json-pointer
相对JSON指针
regex
ECMA-262标准的正则表达式
duration
ISO 8601标准的时长格式
uuid
RFC 4122标准的UUID

Registering a sync format

注册同步格式验证器

A format validator is a function
(input: unknown) => boolean
. Return
true
if valid,
false
if invalid. Return
true
for non-applicable types (e.g., when input is not a string) — z-schema calls format validators for any value type.
格式验证器是一个
(input: unknown) => boolean
类型的函数。验证通过返回
true
,不通过返回
false
。对于不适用的类型(如输入不是字符串)也应返回
true
——z-schema会为任意类型的值调用格式验证器。

Global registration (shared across all instances)

全局注册(所有实例共享)

typescript
import ZSchema from 'z-schema';

ZSchema.registerFormat('postal-code', (value) => {
  if (typeof value !== 'string') return true; // skip non-strings
  return /^\d{5}(-\d{4})?$/.test(value);
});
typescript
import ZSchema from 'z-schema';

ZSchema.registerFormat('postal-code', (value) => {
  if (typeof value !== 'string') return true; // 跳过非字符串类型
  return /^\d{5}(-\d{4})?$/.test(value);
});

Instance-scoped registration

实例级注册

typescript
const validator = ZSchema.create();
validator.registerFormat('postal-code', (value) => {
  if (typeof value !== 'string') return true;
  return /^\d{5}(-\d{4})?$/.test(value);
});
Instance formats override global formats with the same name.
typescript
const validator = ZSchema.create();
validator.registerFormat('postal-code', (value) => {
  if (typeof value !== 'string') return true;
  return /^\d{5}(-\d{4})?$/.test(value);
});
实例级注册的格式会覆盖同名的全局注册格式。

Via options at creation time

创建实例时通过选项注册

typescript
const validator = ZSchema.create({
  customFormats: {
    'postal-code': (value) => typeof value === 'string' && /^\d{5}(-\d{4})?$/.test(value),
    'always-valid': () => true,
    'disable-email': null, // disable the built-in email validator
  },
});
Pass
null
to disable a built-in or globally registered format.
typescript
const validator = ZSchema.create({
  customFormats: {
    'postal-code': (value) => typeof value === 'string' && /^\d{5}(-\d{4})?$/.test(value),
    'always-valid': () => true,
    'disable-email': null, // 禁用内置的邮箱验证器
  },
});
传入
null
可以禁用内置或全局注册的格式验证器。

Registering an async format

注册异步格式验证器

Return
Promise<boolean>
. The validator must be created with
{ async: true }
.
typescript
const validator = ZSchema.create({ async: true });

validator.registerFormat('user-exists', async (value) => {
  if (typeof value !== 'number') return true;
  const user = await db.findUser(value);
  return user != null;
});

// Validate (returns Promise)
try {
  await validator.validate(data, schema);
} catch (err) {
  console.log(err.details);
}
返回
Promise<boolean>
类型的结果。验证器必须通过
{ async: true }
选项创建。
typescript
const validator = ZSchema.create({ async: true });

validator.registerFormat('user-exists', async (value) => {
  if (typeof value !== 'number') return true;
  const user = await db.findUser(value);
  return user != null;
});

// 验证(返回Promise)
try {
  await validator.validate(data, schema);
} catch (err) {
  console.log(err.details);
}

Timeout

超时设置

Async format validators time out after
asyncTimeout
milliseconds (default: 2000). Increase for slow operations:
typescript
const validator = ZSchema.create({
  async: true,
  asyncTimeout: 10000, // 10 seconds
});
Timed-out validators produce an
ASYNC_TIMEOUT
error.
异步格式验证器的默认超时时间为
asyncTimeout
毫秒(默认2000)。对于慢操作可以增加超时时间:
typescript
const validator = ZSchema.create({
  async: true,
  asyncTimeout: 10000, // 10秒
});
超时的验证器会抛出
ASYNC_TIMEOUT
错误。

Format assertion behavior across drafts

不同JSON Schema版本中的格式断言行为

The JSON Schema spec changed how
format
works in newer drafts:
DraftDefault behaviorWith
formatAssertions: true
draft-04/06/07Always asserts (fails on mismatch)Always asserts
draft-2019-09Always asserts (z-schema default)Annotation-only unless vocabulary enabled
draft-2020-12Always asserts (z-schema default)Annotation-only unless vocabulary enabled
z-schema defaults to
formatAssertions: null
(legacy — always assert). To respect the spec's vocabulary-aware behavior for modern drafts:
typescript
const validator = ZSchema.create({ formatAssertions: true });
To disable all format assertions (annotation-only):
typescript
const validator = ZSchema.create({ formatAssertions: false });
JSON Schema规范在新版本中修改了
format
的工作方式:
版本号默认行为开启
formatAssertions: true
后的行为
draft-04/06/07始终执行断言(不匹配则失败)始终执行断言
draft-2019-09始终执行断言(z-schema默认)仅作为注解,除非启用了对应的词汇表
draft-2020-12始终执行断言(z-schema默认)仅作为注解,除非启用了对应的词汇表
z-schema默认使用
formatAssertions: null
(兼容旧版——始终执行断言)。要让现代版本的验证器遵循规范中支持词汇表的行为,可配置:
typescript
const validator = ZSchema.create({ formatAssertions: true });
要禁用所有格式断言(仅作为注解):
typescript
const validator = ZSchema.create({ formatAssertions: false });

Unknown formats

未知格式处理

By default, z-schema reports
UNKNOWN_FORMAT
for unrecognized format names in draft-04/06/07. Modern drafts (2019-09, 2020-12) always silently ignore unknown formats.
To suppress unknown format errors in older drafts:
typescript
const validator = ZSchema.create({ ignoreUnknownFormats: true });
默认情况下,在draft-04/06/07版本中,z-schema会为未识别的格式名称抛出
UNKNOWN_FORMAT
错误。而在现代版本(2019-09、2020-12)中,会自动忽略未知格式。
要在旧版本中抑制未知格式错误:
typescript
const validator = ZSchema.create({ ignoreUnknownFormats: true });

Unregistering a format

注销格式验证器

typescript
// Global
ZSchema.unregisterFormat('postal-code');

// Instance
validator.unregisterFormat('postal-code');
typescript
// 全局注销
ZSchema.unregisterFormat('postal-code');

// 实例级注销
validator.unregisterFormat('postal-code');

Listing formats

列出格式验证器

typescript
// List globally registered custom formats
const customFormats = ZSchema.getRegisteredFormats();

// List all supported formats (built-in + custom) on an instance
const allFormats = validator.getSupportedFormats();

// Check if a specific format is supported
const supported = validator.isFormatSupported('postal-code');
typescript
// 列出全局注册的自定义格式
const customFormats = ZSchema.getRegisteredFormats();

// 列出实例支持的所有格式(内置+自定义)
const allFormats = validator.getSupportedFormats();

// 检查是否支持特定格式
const supported = validator.isFormatSupported('postal-code');

Real-world patterns

实际应用场景

Phone number validation

电话号码验证

typescript
ZSchema.registerFormat('phone', (value) => {
  if (typeof value !== 'string') return true;
  return /^\+?[1-9]\d{1,14}$/.test(value); // E.164 format
});
typescript
ZSchema.registerFormat('phone', (value) => {
  if (typeof value !== 'string') return true;
  return /^\+?[1-9]\d{1,14}$/.test(value); // E.164格式
});

ISO 8601 date (strict)

严格ISO 8601日期验证

typescript
ZSchema.registerFormat('iso-date', (value) => {
  if (typeof value !== 'string') return true;
  const d = new Date(value);
  return !isNaN(d.getTime()) && value === d.toISOString().split('T')[0];
});
typescript
ZSchema.registerFormat('iso-date', (value) => {
  if (typeof value !== 'string') return true;
  const d = new Date(value);
  return !isNaN(d.getTime()) && value === d.toISOString().split('T')[0];
});

Business rule: value from external list

业务规则:验证外部列表中的值

typescript
const validator = ZSchema.create({ async: true });

validator.registerFormat('valid-country', async (value) => {
  if (typeof value !== 'string') return true;
  const countries = await fetchValidCountries();
  return countries.includes(value.toUpperCase());
});
typescript
const validator = ZSchema.create({ async: true });

validator.registerFormat('valid-country', async (value) => {
  if (typeof value !== 'string') return true;
  const countries = await fetchValidCountries();
  return countries.includes(value.toUpperCase());
});

Side-effect: prefill defaults

副作用:填充默认值

Format validators can mutate objects (use with caution):
typescript
ZSchema.registerFormat('fill-defaults', (obj) => {
  if (typeof obj === 'object' && obj !== null) {
    (obj as Record<string, unknown>).createdAt ??= new Date().toISOString();
  }
  return true;
});
格式验证器可以修改对象(谨慎使用):
typescript
ZSchema.registerFormat('fill-defaults', (obj) => {
  if (typeof obj === 'object' && obj !== null) {
    (obj as Record<string, unknown>).createdAt ??= new Date().toISOString();
  }
  return true;
});

Schema usage

Schema使用示例

json
{
  "type": "object",
  "properties": {
    "phone": { "type": "string", "format": "phone" },
    "country": { "type": "string", "format": "valid-country" },
    "zipCode": { "type": "string", "format": "postal-code" }
  }
}
json
{
  "type": "object",
  "properties": {
    "phone": { "type": "string", "format": "phone" },
    "country": { "type": "string", "format": "valid-country" },
    "zipCode": { "type": "string", "format": "postal-code" }
  }
}