校验结果
validate、parse 等函数返回什么:结果由哪些部分组成、数据会发生什么变化、错误的结构如何。以下所有示例都是 tinyval 的真实输出。
结果由什么组成
validate 和 validateAsync 返回一个包含四个字段的对象:
| 字段 | 数据有效时 | 存在错误时 |
|---|---|---|
valid | true | false |
data | 清洗后的数据 | undefined |
errors | {} | 每个字段的第一个错误:{ 'items.0.qty': '...' } |
issues | [] | 全部错误的详细信息:代码、路径、参数、文本 |
parse 和 parseAsync 直接返回 data,或抛出 ValidationError。
数据有效 —— data 中有什么
data 不是输入数据,而是经过 Schema 处理后的结果。示例:
ts
const Order = object({
name: pipe(string(), trim(), minLength(2)),
email: pipe(string(), trim(), toLowerCase(), email()),
age: optional(pipe(coerce.number(), min(18))),
comment: optional(string()),
middleName: optional(nullable(string())),
newsletter: optional(coerce.boolean(), false),
items: array(object({ title: string(), qty: pipe(coerce.number(), min(1)) })),
});
validate(Order, {
name: ' Ivan ',
email: ' IVAN@Mail.RU ',
age: '30',
comment: '',
middleName: null,
items: [{ title: 'Book', qty: '2' }],
isAdmin: true,
});json
{
"valid": true,
"data": {
"name": "Ivan",
"email": "ivan@mail.ru",
"age": 30,
"middleName": null,
"newsletter": false,
"items": [{ "title": "Book", "qty": 2 }]
},
"errors": {},
"issues": []
}| 字段 | 输入 | data 中 | 原因 |
|---|---|---|---|
name | ' Ivan ' | 'Ivan' | trim |
email | ' IVAN@Mail.RU ' | 'ivan@mail.ru' | trim + toLowerCase |
age | '30' | 30 | coerce.number —— 字符串变成了数字 |
comment | '' | 无此字段 | optional 中的空字符串表示“未提供”,字段不会出现在 data 中 |
middleName | null | null | nullable 保留 null |
newsletter | 缺失 | false | 来自 optional(…, false) 的默认值 |
items[0].qty | '2' | 2 | 数组中的 coerce.number |
isAdmin | true | 无此字段 | 多余字段被删除(object 的 strip 模式) |
因此 data 可以直接存入数据库:类型已转换,没有多余字段,TypeScript 类型由 Schema 推导。
存在错误 —— errors 和 issues
ts
validate(Order, { name: 'I', email: 'bad', age: '15', items: [{ title: 'Book', qty: '0' }, { qty: '1' }] });js
{
"valid": false,
"data": undefined,
"errors": {
"name": "Must be at least 2 characters long",
"email": "Invalid email",
"age": "Must be at least 18",
"items.0.qty": "Must be at least 1",
"items.1.title": "Required"
},
"issues": [
{ "code": "minLength", "path": ["name"], "params": { "min": 2 }, "message": "Must be at least 2 characters long" },
{ "code": "email", "path": ["email"], "params": {}, "message": "Invalid email" },
{ "code": "min", "path": ["age"], "params": { "min": 18 }, "message": "Must be at least 18" },
{ "code": "min", "path": ["items", 0, "qty"], "params": { "min": 1 }, "message": "Must be at least 1" },
{ "code": "required", "path": ["items", 1, "title"], "params": {}, "message": "Required" }
]
}errors
“路径 → 第一个错误文本”的对象。便于在字段旁显示错误:errors.email、errors['items.0.qty']。
- 路径用点连接,数组索引为数字:
items.1.title。 - 值本身的错误(Schema 不是对象时)在键
''下:validate(string(), 42).errors→{ '': 'Must be a string' }。 - 每个字段一个错误 —— 按
pipe中规则顺序的第一个。
issues
按 Schema 字段顺序排列的全部错误数组。每个错误包含:
| 字段 | 示例 | 用途 |
|---|---|---|
code | 'minLength' | 错误代码 —— 用于自定义逻辑和翻译。全部代码见 错误信息 |
path | ['items', 0, 'qty'] | 数组形式的路径:索引为数字,便于嵌套表单 |
params | { min: 1 } | 规则参数 —— 用于在客户端生成自定义文本 |
message | 'Must be at least 1' | 已生成的文本,已应用语言、label 和自定义信息 |
字段的全部错误与 Laravel 格式
默认每个字段一个错误。使用 { all: true } 选项时,issues 包含全部错误,flatten 会按字段分组:
ts
const r = validate(object({ password: pipe(string(), minLength(8), email()) }), { password: 'abc' }, { all: true });
r.errors; // { password: 'Must be at least 8 characters long' }
flatten(r.issues); // { password: ['Must be at least 8 characters long', 'Invalid email'] }parse 与 ValidationError
出错时 parse 会抛出 ValidationError —— 其中包含相同的 errors 和 issues,message 合并为一行,便于记录日志:
ts
try {
parse(Order, { name: 'I' });
} catch (e) {
e instanceof ValidationError; // true
e.message; // 'name: Must be at least 2 characters long; email: Required; items: Required'
e.errors; // { name: 'Must be at least 2 characters long', email: 'Required', items: 'Required' }
e.issues; // [{ code, path, params, message }, …]
}Standard Schema
通过 schema['~standard'].validate 得到的是标准格式的结果 —— { value } 或仅包含 message 和 path 的 { issues }:
json
{
"issues": [
{ "message": "Must be at least 2 characters long", "path": ["name"] },
{ "message": "Required", "path": ["email"] },
{ "message": "Required", "path": ["items"] }
]
}TypeScript 中的结果类型
Result<T> 是联合类型:检查 valid 之后,TypeScript 就知道 data 是否存在。详见 TypeScript。
ts
const r = validate(Order, input);
if (r.valid) r.data.items[0].qty; // number
else r.errors; // Record<string, string>