Vue

Валидация форм

Форма регистрации начинается с пары if: проверить, что email не пустой и есть @. Через месяц требований десять: длина пароля, совпадение паролей, формат телефона, обязательность согласия, асинхронная проверка занятости логина. Самописные if расползаются по компоненту, типы ошибок никто не гарантирует, а сообщения дублируются. VeeValidate в связке с Zod даёт схему как единый источник правды: одна Zod-схема описывает и валидацию, и типы данных, и форма знает их без единого as.

  • Регистрация: Zod-схема описывает email, пароль и подтверждение, VeeValidate показывает ошибки под полями
  • Чекаут: одна схема валидирует адрес доставки на клиенте и переиспользуется на сервере
  • Форма настроек профиля: типы данных выводятся из схемы, компонент не дублирует интерфейс руками
  • Онбординг в SaaS: пошаговая форма, где каждый шаг это часть общей Zod-схемы
  • Форма обратной связи: required-поля и формат email описаны декларативно, без россыпи if в обработчике

Предварительные знания

  • Двусторонняя привязка v-model на полях формы из предыдущего урока
  • Базовый TypeScript: интерфейсы, дженерики, вывод типов
  • Понимание идеи схемы данных: описание формы значения и его ограничений

Подходы к валидации

Ручные проверки через if работают на крошечной форме, но плохо масштабируются. Логика валидации размазывается по обработчику, сообщения об ошибках дублируются, а тип объекта ошибок никто не контролирует. Схемный подход разворачивает это: правила описываются декларативно в одном месте, а библиотека формы применяет их и отдаёт типизированные ошибки.

  • Ручные if — Каждое правило это код в обработчике. Объект ошибок собирается вручную, его тип не гарантирован, сообщения дублируются при росте формы
  • Схема (Zod) + VeeValidate — Правила в одной схеме, типы выводятся из неё, ошибки типизированы. Форма масштабируется добавлением полей в схему, а не кода в обработчик
КритерийРучные ifСхема + VeeValidate
Источник правилРазбросан по кодуЕдиная схема
Типы ошибокНе гарантированыВыводятся из схемы
ПереиспользованиеКопированиеИмпорт схемы
МасштабированиеБольше ifПоля в схеме

Схемный подход не означает отказ от собственных правил. Сложная бизнес-логика выражается через refine и superRefine в Zod, поэтому декларативность не упирается в простые проверки длины и формата.

Чем схемный подход к валидации лучше россыпи ручных if при росте формы?

Zod-схема и вывод типов

Zod описывает форму данных как схему: типы полей, ограничения, сообщения. Из той же схемы метод z.infer выводит TypeScript-тип, поэтому интерфейс не пишут руками и он не рассинхронизируется с правилами. Это устраняет потребность в ручных кастах: тип данных формы выводится из схемы автоматически.

Метод safeParse возвращает результат с дискриминантным полем success. Это и есть type guard: в ветке success === true TypeScript сужает тип к валидным данным, в ветке false доступен типизированный error. Никакого non-null и явного каста не требуется, сужение делает сам компилятор.

Правило проекта без any, без non-null и без явных кастов выполняется здесь естественно: z.infer даёт тип, safeParse даёт сужение через discriminated union. Каст не нужен, потому что типы выводятся из схемы.

Как получить TypeScript-тип данных формы из Zod-схемы без ручного интерфейса?

VeeValidate с Zod-схемой

VeeValidate подключает Zod-схему через адаптер toTypedSchema. useForm принимает типизированную схему и возвращает handleSubmit, errors и состояние формы. useField связывает конкретное поле с v-model и отдаёт его значение и сообщение об ошибке. Типы при этом выводятся из той же схемы.

toTypedSchema это мост между Zod и VeeValidate: он превращает Zod-схему в формат, который понимает VeeValidate, и протягивает выведенные типы в useForm. Дженерик useField<string> задаёт тип значения поля без приведения.

handleSubmit вызывает переданный колбэк только после успешной валидации всей схемы. Поэтому внутри колбэка values уже валидны и типизированы, и проверять их повторно или приводить тип не нужно.

Зачем Zod-схему оборачивают в toTypedSchema перед передачей в useForm?

Вывод типизированных сообщений

Объект errors из useForm типизирован по ключам схемы: errors.email и errors.password существуют, а опечатка errors.emial вызовет ошибку компиляции. Сообщения берутся из схемы, поэтому текст ошибки задаётся один раз в Zod и не дублируется в шаблоне. Так вывод сообщений остаётся типобезопасным.

Поскольку errors типизирован, IDE подсказывает доступные ключи, а несуществующее поле не пройдёт сборку. Это убирает класс багов, где сообщение об ошибке висит на несуществующем поле из-за опечатки и потому никогда не показывается.

ЧтоОткуда берётсяГарантия
Текст ошибкиmessage в Zod-схемеОдин источник, не дублируется
Ключ поля errors.xИмена полей схемыПроверяется компилятором
Тип values при сабмитеz.infer схемыБез any и каста

Если сообщение об ошибке не показывается, при типизированном errors первым делом проверяют не опечатку в ключе (её поймает компилятор), а совпадает ли path в refine с именем поля в шаблоне.

Какое преимущество даёт типизированный объект errors из useForm?

Связь с другими темами

Урок про декларативную валидацию. Дальше схему применяют к серверным данным и переиспользованию:

  • Загрузка данных — Та же Zod-схема валидирует ответ сервера, а не только ввод формы
  • VueUse — Тот же принцип: брать проверенные composable-решения вместо ручного кода

Итог

  • Самописные if плохо масштабируются: типы ошибок не гарантированы, логика и сообщения дублируются по компоненту
  • Zod описывает схему данных декларативно, а z.infer выводит из неё TypeScript-тип без ручного дублирования интерфейса
  • VeeValidate с toTypedSchema подключает Zod-схему: useForm и useField дают значения, ошибки и состояние полей
  • Ошибки типизированы и берутся из схемы, поэтому компонент не нуждается в any, non-null или явных кастах
  • Одна схема переиспользуется на клиенте и сервере, что убирает рассинхрон правил валидации между ними

Связанные уроки

  • vue-30-data-fetching — Валидную форму отправляют на сервер, и ответ сервера тоже стоит валидировать той же Zod-схемой
  • vue-34-vueuse — И VeeValidate, и VueUse это переиспользование проверенных composable-решений вместо самописного кода
Валидация форм

0

1

Войти