Типизация схем в связке с YupResolver строится вокруг строгого соответствия между описанием валидационной схемы и типом данных формы. Основная цель — добиться того, чтобы структура данных, приходящих в форму, была предсказуемой на уровне TypeScript, а не только в runtime-валидации.
В основе работы лежит интеграция двух компонентов: схемы валидации из
библиотеки Yup и резолвера из экосистемы react-hook-form через пакет
resolver’ов, в частности @hookform/resolvers/yup.
Yup-схема описывает форму данных декларативно, например:
TypeScript-тип, в свою очередь, должен отражать эту структуру максимально точно.
Ключевая проблема без типизации — рассинхронизация:
Решение заключается в том, чтобы выводить тип данных непосредственно из схемы.
Схема строится через объектные описания:
string()number()boolean()array()object()Пример логической структуры:
user
В Yup это выражается как объектная схема, которая становится источником типов.
Базовый механизм типизации основан на утилитарном типе:
InferType<typeof schema> из YupОн позволяет автоматически извлечь структуру данных:
stringnumberT | undefinedT | nullТаким образом, схема становится единым источником истины.
Резолвер выполняет роль адаптера между:
Он принимает схему и возвращает функцию, которую форма использует при сабмите и валидации:
Важно, что сам YupResolver не влияет на TypeScript напрямую, он работает на runtime-уровне, но его использование предполагает строго типизированную схему.
Тип данных формы связывается с схемой следующим образом:
Логическая цепочка:
Это исключает необходимость ручного описания интерфейсов.
name: yup.string().required()
Результирующий тип:
stringЕсли поле не обязательно:
string | undefinedage: yup.number().required()
Тип:
numberОсобенность: Yup различает NaN и undefined,
поэтому тип может расширяться при неполной валидации.
isActive: yup.boolean()
Тип:
booleanПри отсутствии значения:
boolean | undefinedМассивы в Yup описываются через array().of(...).
Пример:
tags: yup.array().of(yup.string().required())
Типизация:
string[]Если массив опционален:
string[] | undefinedПри nullable:
string[] | nullОсобенность: вложенные схемы полностью рекурсивно типизируются.
Объекты являются ключевым элементом типизации схем.
user: yup.object({
name: yup.string().required(),
age: yup.number().required()
})
Результат:
{
name: string;
age: number;
}
Если объект опционален:
user?: {
name: string;
age: number;
}
Yup поддерживает глубокую вложенность объектов:
profile: yup.object({
contacts: yup.object({
email: yup.string().email().required()
})
})
Типизация становится рекурсивной:
{
profile: {
contacts: {
email: string;
};
};
}
На этом уровне важна согласованность всех вложенных
InferType.
Типизация сильно зависит от модификаторов схемы:
undefinednullundefined из типаКомбинации приводят к следующим итогам:
T | undefinedT | nullT | null | undefinedYup позволяет описывать условные схемы через when.
Пример:
type: yup.mixed().oneOf(['a', 'b']),
data: yup.object().when('type', {
is: 'a',
then: yup.object({ value: yup.string() }),
otherwise: yup.object({ count: yup.number() })
})
Типизация в этом случае становится объединением:
| { type: 'a'; dat a: { value: string } }
| { type: 'b'; dat a: { count: number } }
Такие конструкции требуют внимательного контроля, поскольку автоматическое выведение типов может быть менее точным.
Несмотря на мощь InferType, существуют ограничения:
transform() не всегда корректно
отражаются в типахwhen() может требовать ручной
корректировкиВ таких случаях типизация дополняется вручную через пересечение или переопределение типов.
Хотя типизация работает на этапе компиляции, YupResolver действует в runtime:
Типы при этом служат контрактом:
Несоответствие между ними приводит к логическим ошибкам, которые TypeScript не всегда способен поймать без строгой схемной дисциплины.
Устойчивый подход строится по принципу:
Таким образом обеспечивается единая точка правды, где Yup определяет структуру, а TypeScript лишь отражает её без дублирования описаний.