Создание собственных правил в Validator.js основано на расширении набора функций валидации за счёт пользовательских проверок, которые соответствуют специфике предметной области. Библиотека изначально предоставляет широкий набор готовых проверок строк, чисел, дат и других типов данных, однако реальные проекты часто требуют более строгих или нестандартных условий.
Основной подход к расширению логики проверки строится вокруг функции, которая принимает значение и возвращает булево значение или сообщение об ошибке. Такие функции могут быть как чистыми (без побочных эффектов), так и контекстными, если требуется доступ к дополнительным параметрам.
Типовой контракт пользовательского правила:
function customRule(value) {
return true; // или false
}
Расширенные варианты предполагают возврат строки с ошибкой:
function customRule(value) {
if (value < 10) {
return 'Значение должно быть не меньше 10';
}
return true;
}
Практика выделения правил в отдельные модули позволяет формировать переиспользуемую систему валидации. Каждое правило представляет собой независимую функцию, что упрощает тестирование и масштабирование.
const isEvenNumber = (value) => {
if (typeof value !== 'number') {
return 'Значение должно быть числом';
}
return value % 2 === 0 || 'Число должно быть чётным';
};
Такая форма позволяет использовать правило в различных контекстах без привязки к конкретной структуре данных.
При увеличении количества проверок возникает необходимость их комбинирования. Композиция позволяет объединять несколько функций в одну цепочку проверки.
const composeValidators = (...validators) => (value) => {
for (const validator of validators) {
const result = validator(value);
if (result !== true) {
return result;
}
}
return true;
};
Пример использования:
const isPositive = (value) => value > 0 || 'Должно быть положительным';
const isInteger = (value) => Number.isInteger(value) || 'Должно быть целым числом';
const validateNumber = composeValidators(isPositive, isInteger);
В случаях, когда требуется интеграция с существующей логикой Validator.js, пользовательские правила часто оборачиваются вокруг стандартных функций. Это позволяет сохранить единый стиль проверки данных.
import validator from 'validator';
const isCorporateEmail = (email) => {
if (!validator.isEmail(email)) {
return 'Некорректный email';
}
const domain = email.split('@')[1];
return domain.endsWith('company.com') || 'Разрешены только корпоративные адреса';
};
Такой подход объединяет базовую проверку формата и бизнес-логику.
Фабрики позволяют создавать параметризованные правила, которые адаптируются под входные данные.
const minLength = (min) => (value) => {
if (typeof value !== 'string') {
return 'Значение должно быть строкой';
}
return value.length >= min || `Минимальная длина ${min} символов`;
};
Применение:
const validateUsername = minLength(6);
Фабричный подход особенно эффективен при работе с формами, где требования могут отличаться в зависимости от поля.
Некоторые проверки требуют доступа к внешнему состоянию или другим полям объекта. В таких случаях правило принимает дополнительный параметр контекста.
const matchField = (fieldName) => (value, context) => {
return value === context[fieldName] || 'Значения не совпадают';
};
Пример контекста:
const context = {
password: '123456'
};
const confirmPasswordValidator = matchField('password');
confirmPasswordValidator('123456', context);
Перед применением пользовательских правил часто выполняется нормализация данных. Это снижает количество ошибок, связанных с типами и форматами.
const normalizeString = (value) => {
if (typeof value !== 'string') {
return '';
}
return value.trim().toLowerCase();
};
После нормализации значение становится предсказуемым для последующих проверок.
При работе с объектами и массивами пользовательские правила расширяются до рекурсивных проверок.
const validateArray = (rule) => (arr) => {
if (!Array.isArray(arr)) {
return 'Значение должно быть массивом';
}
for (const item of arr) {
const result = rule(item);
if (result !== true) {
return result;
}
}
return true;
};
Такой подход позволяет переиспользовать базовые валидаторы для сложных структур.
При построении системы пользовательских правил важно учитывать порядок их выполнения. Ошибки ранней стадии должны прерывать дальнейшие проверки, чтобы избежать лишних вычислений.
const validate = (value, rules) => {
for (const rule of rules) {
const result = rule(value);
if (result !== true) {
return result;
}
}
return true;
};
Порядок правил становится частью логики, а не просто технической деталью.
Пользовательские правила часто используются в связке с формами, API-слоем или бизнес-логикой. В таких случаях они отделяются от UI и хранятся в отдельном слое доменной валидации.
const userValidationRules = {
username: composeValidators(
minLength(6),
(v) => /^[a-z0-9_]+$/.test(v) || 'Недопустимые символы'
),
age: (v) => v >= 18 || 'Возраст должен быть не менее 18'
};
Такой подход обеспечивает единообразие проверки данных независимо от источника ввода.
При создании собственных правил часто возникают типовые проблемы:
Корректная архитектура предполагает изоляцию правил и их повторное использование через композицию и фабрики.
При росте количества проверок целесообразно вводить слой регистрации правил, где каждая функция имеет идентификатор и может быть динамически подключена.
const rulesRegistry = new Map();
const registerRule = (name, fn) => {
rulesRegistry.set(name, fn);
};
const getRule = (name) => rulesRegistry.get(name);
Такой механизм позволяет централизованно управлять набором правил и упрощает поддержку крупных систем валидации.