Одним из наиболее частых граничных случаев при работе с библиотекой
валидации Vest является обработка отсутствующих или неопределённых
значений. В реальных формах данные редко поступают в полностью
нормализованном виде: поля могут быть undefined,
null, пустыми строками или вовсе отсутствовать в объекте
состояния.
Внутренняя логика валидации должна учитывать различие между этими состояниями:
undefined — поле не заданоnull — поле явно очищено"" — пользователь ввёл пустую строкуТипичная ошибка заключается в попытке валидировать значение без предварительной нормализации:
rule('email', 'Email обязателен', () => {
enforce(form.email).isNotEmpty();
});
Такой код падает или даёт неконсистентный результат, если
form.email отсутствует.
Корректная обработка включает явное приведение входных данных:
const value = form.email ?? '';
rule('email', 'Email обязателен', () => {
enforce(value).isNotEmpty();
});
Граничный случай здесь заключается не только в пустоте, но и в потере реактивной связи, когда поле существует, но не обновляется в состоянии.
Числа в формах представляют особый класс граничных случаев, особенно когда данные приходят из HTML input, где фактический тип всегда строка.
Основные проблемные ситуации:
"" (пустой ввод)"0" (ложно трактуется как falsy в некоторых
проверках)NaNNumber.MAX_SAFE_INTEGER и
выше)Ошибка, часто встречающаяся при валидации:
rule('age', 'Возраст обязателен', () => {
enforce(form.age).isNotEmpty();
});
Если пользователь вводит 0, проверка может быть ошибочно
интерпретирована как провал, если используется нестрогое сравнение.
Корректная стратегия — разделение проверки наличия и проверки диапазона:
const age = Number(form.age);
rule('age', 'Возраст обязателен', () => {
enforce(form.age).isNotEmpty();
});
rule('age', 'Возраст должен быть положительным', () => {
enforce(age).greaterThan(0);
});
Особое внимание требуется к NaN, который не равен самому
себе и может незаметно проходить проверки:
Number('abc') // NaN
Проверка должна учитывать это явно:
enforce(age).isNotNaN();
Граничный случай, возникающий при пошаговом заполнении форм, когда часть полей ещё не доступна для пользователя, но уже присутствует в состоянии.
Типичная проблема — каскадные ошибки валидации:
Пример некорректной зависимости:
rule('confirmPassword', 'Пароли должны совпадать', () => {
enforce(form.confirmPassword).equals(form.password);
});
Если password ещё не введён, результат становится
неконтролируемым.
Правильный подход — введение условной валидации:
rule('confirmPassword', 'Пароли должны совпадать', () => {
if (!form.password) return;
enforce(form.confirmPassword).equals(form.password);
});
Либо использование стадий валидации:
При усложнении логики появляются ситуации, когда разные правила противоречат друг другу.
Примеры:
Типичный конфликт:
rule('username', 'Обязательно', () => {
enforce(form.username).isNotEmpty();
});
rule('username', 'Минимум 3 символа', () => {
enforce(form.username).longerThanOrEquals(3);
});
Если поле пустое, оба правила возвращают ошибку, хотя логически достаточно одной.
Для управления конфликтами применяют:
Асинхронные проверки (например, проверка уникальности логина) создают особый класс проблем:
Классическая проблема:
alexalex2alex приходит позже и перезаписывает
результатБез контроля состояния это приводит к ложным ошибкам.
Решение — привязка результата к текущему значению:
let lastChecked = '';
rule('username', 'Уже существует', async () => {
const value = form.username;
lastChecked = value;
const exists = await api.checkUsername(value);
if (lastChecked !== value) return;
enforce(exists).equals(false);
});
Массивы часто используются для динамических полей (email-адреса, телефоны, теги). Основные проблемы:
undefined элементамиОшибка:
form.emails.forEach(email => {
enforce(email).matches(/@/);
});
Если emails равен undefined, возникает
исключение.
Базовая защита:
const emails = form.emails ?? [];
emails.forEach(email => {
enforce(email).matches(/@/);
});
Дополнительный граничный случай — разная валидность элементов:
Одним из наименее очевидных граничных случаев является изменение данных в процессе выполнения правил.
Например:
rule('field', 'Проверка', () => {
form.field = form.field.trim();
enforce(form.field).isNotEmpty();
});
Такая практика приводит к:
Правильный подход — разделение трансформации и валидации:
Зависимые поля (country → city, category → subcategory) создают сложные сценарии:
Типичный случай:
Если не сбрасывать зависимое поле, валидация работает с некорректной комбинацией данных.
Подход:
Во время первичной загрузки формы состояние часто неполное:
Если валидация запускается до полной инициализации, возникают ложные ошибки.
Особенно критично это для:
Решение заключается в разделении фаз:
Хотя JavaScript не имеет классического переполнения целых чисел, существуют логические пределы:
Пример:
0.1 + 0.2 !== 0.3
В контексте валидации это может приводить к ложным отрицаниям при проверке сумм, балансов или процентов.
Для устойчивости применяются:
const EPS = 0.0001;
enforce(Math.abs(sum - expected)).lessThan(EPS);
Ошибки валидации не являются статичными. Они могут:
Особенно проблемно это в динамических формах, где правила пересчитываются частично.
Подходы: