# Обучение
# Простой пример
v9s создает цепочку правил. Исполнение цепочки начинается с конца и заканчивается в начале цепочки (см. примеры ниже). Вы можете интегрировать собственные правила в цепочку при помощи метода use
. Для валидации значение вызовите метод check
.
Просто пример:
import v9s from 'v9s';
// создаем экземпляр валидатора с правилами
const validator = v9s.lte(100).gte(10);
const small = validator.check(1); // проверяем маленькое значение
console.log(small); // false
const big = validator.check(110); // проверяем большое значение
console.log(big); // false
const normal = validator.check(50); // проверяем нормальное значение
console.log(normal); // true
# Сообщения об ошибках
Часто необходимо добавить текст сообщения вместо результата в виде true
или false
. Легко, просто добавим второй строковый параметр во встроенное правило или метод use
. Давайте перепишем предыдущий пример:
import v9s from 'v9s';
// создаем экземпляр валидатора с правилами и сообщениями об ошибках.
const validator = v9s.lte(100, 'too big').gte(10, 'too small');
const small = validator.check(1); // проверяем маленькое значение
console.log(small); // 'too small'
const big = validator.check(110); // проверяем большое значение
console.log(big); // 'too big'
const normal = validator.check(50); // проверяем нормальное значение
console.log(normal); // true
Если вам нужно использовать другой формат сообщения - задайте его тип (кроме boolean
или Function
):
import v9s from 'v9s';
enum ValidationError {
tooSmall,
tooBig
}
const validator = v9s.lte<ValidationError>(100, tooBig).gte(10, tooSmall);
const small = validator.check(1); // check small value
console.log(small); // 0
const big = validator.check(110); // check big value
console.log(big); // 1
const normal = validator.check(50); // check normal value
console.log(normal); // true
# Последовательности
Но что делать, если нам нужно получать разные сообщения об ошибках для одного и того же правила, но с некоторым набором пороговых значений? Что ж, пришло время вспомнить последовательность выполнения цепочки.
import v9s from 'v9s';
// создаем экземпляр валидатора с сортированной цепочкой правил и сообщениями об ошибках.
const validator = v9s.gte(100, 'small').gte(10, 'very small');
const verySmall = validator.check(9); // проверяем очень маленькое значение
console.log(verySmall); // 'very small'
const small = validator.check(50); // проверяем маленькое значение
console.log(small); // 'small'
const normal = validator.check(110); // проверяем нормальное значение
console.log(normal); // true
# Инверсия
Иногда нужно инвертировать результат работы правила. Легко! Встречайте метод not
:
import v9s from 'v9s';
// создадим экземпляр валидатора с инвертированным правилом
const validator = v9s.not().string();
const isNumber = validator.check(42); // проверяем число
console.log(isNumber); // true
const isString = validator.check('42'); // проверяем строку
console..log(isString); // false (не строка)
# Модификатор опциональности
В противном случае можно разрешить undefined
значения:
import v9s from 'v9s';
const validator = v9s.string().optional();
const isNumber = validator.check(42); // проверяем число
console.log(isNumber); // false
const isString = validator.check('42'); // проверяем строку
console.log(isString); // true
const isNotDefined = validator.check(undefined); // проверяем undefined
console.log(isNotDefined); // true
Модификатор optional
применяется только к конкретному правилу, следующее правило его игнорирует.
# Композиция
Когда необходимо добавить альтернативное условие - самое время использовать метод or
:
import v9s from 'v9s';
const validator = v9s.string().optional().or(v9s.number());
const isString = validator.check('42');
console.log(isString); // true
const isNotDefined = validator.check(undefined);
console.log(isNotDefined); // true
const isNumber = validator.check(42);
console.log(isNumber); // true
const isBoolean = validator.check(true);
console.log(isBoolean); // false
const isNull = validator.check(null);
console.log(isNull); // false, потому что null !== undefined и может быть корректным значением
Обычно не требуется сохранять экземпляр валидатора, только функцию проверки:
import v9s from 'v9s';
const check = v9s.string().optional().or(v9s.number()).check;
console.log(check('42')); // true
# Внешние правила
Конечно, библиотека содержит минимальное количество правил внутри, но можно использовать внешние правила. Внешнее правило должно быть совместимо с этой сигнатурой:
type Rule = (value: any, context: any) => boolean;
Давайте создадим собственное правило, которое проверяет, является ли строковое значение целым числом.
import v9s from 'v9s';
const integer = (value: string) => /^[0-9]+$/.test(value); // проверяем целочисленную строку
const check = v9s.use(integer).check;
console.log(check('42')); // true
console.log(check('42a')); // false
# Модификаторы
Хорошо, мы уверены, что наше значение является целочисленной строкой. Теперь добавим диапазон допустимых значений и преобразуем значение в тип number
при помощи Modifier
:
import v9s from 'v9s';
const integer = (value: string) => /^[0-9]+$/.test(value);
const modify = (value: string) => Number(value); // пробразование сроки в число
const check = v9s.between(10, 100).use(integer, undefined, modify).check;
console.log(check('42')); // true
console.log(check('9')); // false
console.log(check('110')); // false
Сигнатура модификатора:
type Modifier = (value: any, context: any) => any;
# Интернационализация
Вместо строковых сообщений можно использовать фабрики сообщений. Эта функция может быть полезна для интернационализированных приложений.
import v9s from 'v9s';
enum Lang {
de,
en,
ru
}
let lang: Lang = Lang.en;
const errorMessageFactory = () => {
switch (lang) {
case Lang.de:
return 'Ungültiger Wert';
case Lang.ru:
return 'Неверное значение';
default:
return 'Invalid value';
}
};
const check = v9s.between(10, 100, errorMessageFactory).check;
console.log(check(50)); // true
console.log(check(110)); // 'Invalid value'
lang = Lang.de;
console.log(check(110)); // 'Ungültiger Wert'
lang = Lang.ru;
console.log(check(110)); // 'Неверное значение'
# Контекст
Вы видели параметр context
в предыдущих примерах. Это объект (по умолчанию: {}
), который перемещается между правилами в цепочке и позволяет обмениваться данными между ними. Контекст может содержать промежуточные вычисления, другие поля субъекта и так далее. В следующем примере промежуточные вычисления перемещаются между правилами:
import v9s from 'v9s';
const checkForDuplicates = function (value: number[], context: { sorted?: number[] }) {
const sorted = value.slice().sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
const noDuplicates = sorted.every((v, i) => !i || sorted[i - 1] !== v);
if (noDuplicates) context.sorted = sorted;
return noDuplicates;
};
const checkMinimum = function (minimum: number, value: number[], context: { sorted: number[] }) {
const { sorted } = context;
return sorted.length > 0 && minimum <= sorted[0];
};
const checkMaximum = function (maximum: number, value: number[], context: { sorted: number[] }) {
const { sorted } = context;
return sorted.length > 0 && maximum >= sorted[sorted.length - 1];
};
const check = v9s.use(checkMaximum.bind(undefined, 100)).use(checkMinimum.bind(undefined, 10)).use(checkForDuplicates).check;
console.log(check([])); // false - пустой
console.log(check([1, 6, 4, 2, 1])); // false - дубликаты `1`
console.log(check([1, 6, 4, 2])); // false - 1 < 10
console.log(check([10, 60, 105, 40, 20])); // false - 105 > 100
console.log(check([10, 60, 40, 20])); // true
Еще один вариант применения контекста - условная проверка с привязкой к другим полям объекта. Поля value
и name
интерфейса имеют значение только тогда, когда оба не пусты. В следующем примере аргумент контекста вручную передается в функцию check
.
import v9s from 'v9s';
interface Data {
name: string;
value: string;
}
const checkNameRule = function (value: string, context: Data) {
return (!value && !context.value) || value.length > 0;
};
const checkValueRule = function (value: string, context: Data) {
return (!value && !context.name) || /^[0-9]+$/.test(value);
};
const checkName = v9s.use(checkNameRule).check;
const checkValue = v9s.use(checkValueRule).check;
const empty = { name: '', value: '' };
console.log(checkName(empty.name, empty), checkValue(empty.value, empty)); // true, true
const emptyName = { name: '', value: '42' };
console.log(checkName(emptyName.name, emptyName), checkValue(emptyName.value, emptyName)); // false, true
const emptyValue = { name: 'the answer', value: '' };
console.log(checkName(emptyValue.name, emptyValue), checkValue(emptyValue.value, emptyValue)); // true, false
const filled = { name: 'the answer', value: '42' };
console.log(checkName(filled.name, filled), checkValue(filled.value, filled)); // true, true