Skip to content

Результат проверки ​

Что возвращают validate, parse и остальные функции: из чего состоит результат, что происходит с данными и как устроены ошибки. Все примеры ниже — настоящий вывод tinyval.

Из чего состоит результат ​

validate и validateAsync возвращают объект из четырёх полей:

ПолеЕсли данные верныЕсли есть ошибки
validtruefalse
dataОчищенные данныеundefined
errors{}Первая ошибка каждого поля: { 'items.0.qty': '...' }
issues[]Все ошибки подробно: код, путь, параметры, текст

parse и parseAsync возвращают сразу data или бросают ValidationError.

Данные проверены — что в data ​

data — это не входные данные, а то, что получилось после схемы. Пример:

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'30coerce.number — строка стала числом
comment''нет поляПустая строка в optional — «не передано», поле не попадает в data
middleNamenullnullnullable сохраняет null
newsletterнетfalseЗначение по умолчанию из optional(…, false)
items[0].qty'2'2coerce.number внутри массива
isAdmintrueнет поляЛишнее поле удалено (object в режиме strip)

Итого data можно сразу сохранять в базу: типы приведены, лишнего нет, тип TypeScript выведен из схемы.

Есть ошибки — 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.
  • Ошибка самого значения (схема не объект) — под ключом '': validate(string(), 42).errors → { '': 'Must be a string' }.
  • На каждое поле — одна ошибка, первая по порядку правил в pipe.

issues ​

Массив всех ошибок в порядке полей схемы. У каждой:

ПолеПримерЗачем
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 } или { issues } только с message и path:

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>

См. также ​