В архитектуре Vest типизация строится вокруг идеи предсказуемого
вывода результата проверки и строгого описания правил валидации через
цепочки декларативных вызовов. Ключевым элементом, влияющим на
типобезопасность правил, выступает enforce — слой
утверждений, который формирует единый механизм проверки значений и их
соответствия ожиданиям.
Типизация в Vest опирается на три уровня:
Каждый уровень связан с другим через выводимые типы. Основная цель — исключить ситуации, при которых результат проверки теряет связь с исходной структурой данных.
Тип данных не описывается отдельно в виде строгой схемы, но выводится
через контекст выполнения suite, где каждый ключ поля
становится частью типовой модели результата.
При объявлении набора правил проверок типизация начинает формироваться автоматически:
suite('user', () => {
test('email', 'Email is invalid', () => {
enforce(values.email).isNotBlank();
});
test('age', 'Age is invalid', () => {
enforce(values.age).greaterThan(18);
});
});
В контексте TypeScript каждый test становится источником
вывода типа для соответствующего поля. Если поле используется в
нескольких проверках, его тип объединяется через пересечение
ограничений.
enforce
как ядро типобезопасных утвержденийenforce — это не просто рантайм-утверждение. В
типизированной модели Vest он выступает как функция, возвращающая строго
типизированный объект-assertion.
Каждый метод цепочки:
Пример:
enforce(value).isString().longerThan(3);
Здесь происходит последовательное сужение типа:
isString() — гарантирует, что значение рассматривается
как stringlongerThan(3) — добавляет числовое ограничение, не
изменяя базовый тип, но уточняя допустимый диапазонenforceКаждый метод enforce возвращает новый типизированный
контекст:
unknownisNumber(): numberisNotEmpty(): number | string с
ограничением непустого значенияmatches(regex): строка с дополнительным
constraintТакой подход позволяет TypeScript выполнять статический контроль корректности цепочек:
Внутренне enforce использует generics для сохранения
исходного типа:
function enforce<T>(value: T): EnforceAssertion<T>
Каждый метод возвращает новый тип:
isString(): EnforceAssertion<string>
isNumber(): EnforceAssertion<number>
При этом сохраняется связь с исходным значением, что позволяет компилятору отслеживать несоответствия на уровне сборки.
Пользовательские проверки интегрируются в систему через расширение
enforce:
enforce.extend({
isEven(value: number) {
return value % 2 === 0;
}
});
Типизация таких расширений требует явного указания сигнатур:
После расширения метод становится частью типизированной цепочки:
enforce(value).isNumber().isEven();
Если метод применён к несовместимому типу, TypeScript фиксирует ошибку ещё до выполнения.
Каждое правило валидации связано с типом ошибки:
string — текстовое сообщениеobject — структурированная ошибкаarray — набор ошибок по подполямПример типизации ошибки:
test('password', 'Too weak password', () => {
enforce(values.password).longerThan(8);
});
Тип результата связывает поле password с типом ошибки
string.
Результат выполнения suite представляет собой объект, где каждая ошибка строго ассоциирована с полем:
{
email: string | undefined;
age: string | undefined;
}
TypeScript выводит этот тип автоматически на основе структуры
test-ов. Если поле не имеет ошибок, значение становится
undefined.
В более сложных сценариях enforce используется для формирования условных типов:
enforce(value)
.isString()
.when(v => v.startsWith('A'), e => e.longerThan(5));
Здесь тип сужается в зависимости от условия, и TypeScript фиксирует возможные ветвления:
stringПри работе с вложенными объектами типизация распространяется рекурсивно:
test('user.address.city', 'Invalid city', () => {
enforce(values.user.address.city).isString();
});
Типовая модель автоматически строит путь
user.address.city, сохраняя строгую связь между
вложенностью данных и результатом проверки.
Несмотря на высокий уровень статической проверки, существуют ограничения:
Тем не менее система сохраняет предсказуемость благодаря
централизованному механизму вывода через enforce.
Если тип значения не соответствует цепочке enforce,
происходит:
Пример несовместимости:
enforce(value).isNumber().longerThan(3);
Метод longerThan недоступен для числового типа в строгой
модели, что фиксируется на этапе компиляции.
В рамках Vest строгая типизация через enforce не
является дополнительным слоем, а встроена в ядро системы. Она
определяет:
Такой подход позволяет рассматривать validation suite как типизированную программу, где каждое утверждение одновременно является и логикой выполнения, и частью статической модели данных.