Миграция с других библиотек валидации

YupResolver представляет собой адаптер, обеспечивающий интеграцию схем валидации Yup с библиотеками управления формами, прежде всего с React Hook Form. Основная задача слоя resolver — преобразование результата валидации схемы в унифицированный формат ошибок, понятный форме.

В контексте миграции ключевым становится не только синтаксис схем, но и поведенческая модель: синхронная и асинхронная валидация, структура ошибок, кастомные трансформации данных и стратегия выполнения валидации (onChange, onBlur, onSubmit).


Общие принципы миграции между библиотеками валидации

Переход на YupResolver затрагивает несколько слоёв логики:

1. Структура схемы

  • объектная модель Yup
  • цепочечные методы (string().required().min())
  • декларативные правила вместо императивных функций

2. Модель ошибок

  • унифицированный формат Yup
  • вложенные пути (path)
  • массивы ошибок или первая ошибка на поле

3. Асинхронность

  • Yup поддерживает асинхронные тесты через test(async () => {})
  • часть библиотек использует синхронные валидаторы по умолчанию

4. Трансформация данных

  • transform() в Yup заменяет post-processing в кастомных схемах

Миграция с Zod

Zod и Yup концептуально схожи, но различаются подходом к типизации и композиции схем.

Сопоставление базовых типов

// Zod
const schema = z.object({
  email: z.string().email(),
  age: z.number().min(18)
});
// Yup
import * as yup from 'yup';

const schema = yup.object({
  email: yup.string().email().required(),
  age: yup.number().min(18).required()
});

Отличия, влияющие на миграцию

1. Strict parsing vs coercion

  • Zod по умолчанию строже к типам
  • Yup часто приводит типы автоматически

Решение при миграции:

age: yup.number().transform(value => Number(value))

2. Optional/nullable

  • Zod: .optional(), .nullable()
  • Yup: .notRequired(), .nullable()

3. Error mapping Zod возвращает структурированные issues, Yup — ValidationError с inner[].

React Hook Form resolver:

import { yupResolver } from '@hookform/resolvers/yup';

Миграция с Joi

Joi ориентирован на серверную валидацию и более декларативен в стиле описания схем.

Базовое сопоставление

// Joi
const schema = Joi.object({
  username: Joi.string().min(3).required(),
  age: Joi.number().min(18)
});
// Yup
const schema = yup.object({
  username: yup.string().min(3).required(),
  age: yup.number().min(18)
});

Ключевые различия

1. Сообщения об ошибках

  • Joi: детализированные message templates
  • Yup: кастомизация через message аргументы
yup.string().required('Поле обязательно')

2. Рекурсивные структуры Joi поддерживает более сложные описания вложенных схем, но Yup проще интегрируется с фронтендом.


3. AbortEarly

  • Joi: abortEarly: false
  • Yup: abortEarly: false в validate
schema.validate(data, { abortEarly: false });

Миграция с кастомных валидаторов

Многие проекты используют собственные функции:

function validate(values) {
  const errors = {};
  if (!values.email) errors.email = 'required';
  return errors;
}

Перенос логики в Yup

1. Простые проверки

email: yup.string().email().required()

2. Сложные правила через test

password: yup.string().test(
  'password-strength',
  'Слабый пароль',
  value => /[A-Z]/.test(value) && /[0-9]/.test(value)
)

3. Зависимые поля

confirmPassword: yup
  .string()
  .oneOf([yup.ref('password')], 'Пароли не совпадают')

Интеграция с React Hook Form через YupResolver

Основной сценарий миграции связан с заменой ручной валидации на resolver.

import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';

const form = useForm({
  resolver: yupResolver(schema)
});

Структура ошибок

React Hook Form ожидает:

{
  fieldName: {
    type: string,
    message: string
  }
}

YupResolver преобразует:

ValidationError -> RHFErrorMap

Миграция асинхронной валидации

Пример асинхронной проверки уникальности

username: yup.string().test(
  'unique',
  'Уже существует',
  async value => {
    const res = await api.checkUsername(value);
    return res.available;
  }
)

Отличия от других библиотек

  • Zod требует ручного async refinement
  • Joi чаще используется серверно
  • кастомные валидаторы требуют явного управления Promise

Преобразование схем сложных объектов

Вложенные структуры

const schema = yup.object({
  user: yup.object({
    name: yup.string().required(),
    address: yup.object({
      city: yup.string().required()
    })
  })
});

Массивы

items: yup.array().of(
  yup.object({
    id: yup.number(),
    title: yup.string().required()
  })
)

Типичные проблемы миграции

1. Неявное приведение типов

Yup может преобразовывать строки в числа:

yup.number().required()

Решение:

strict: true

2. Различие поведения required

  • пустая строка в Yup не всегда считается отсутствием значения
  • требуется явная настройка
yup.string().trim().required()

3. Потеря структуры ошибок

При переходе с кастомных валидаторов часто теряется:

  • группировка ошибок
  • приоритет ошибок
  • кастомные ключи

Решение — использование inner массива ValidationError.


4. Несовместимость кастомных типов

TypeScript схемы Zod не конвертируются напрямую в Yup — требуется ручное пересоздание.


Стратегии постепенной миграции

1. Гибридный подход

resolver: async (values) => {
  try {
    await yupSchema.validate(values);
  } catch (e) {
    return zodCompatibleMapper(e);
  }
}

2. Разделение схем по модулям

  • сначала простые формы
  • затем сложные зависимости
  • затем асинхронные проверки

3. Изоляция слоя валидации

Вынос схем в отдельный модуль:

/validation
  yup/
  zod/
  joi/

Производственные нюансы использования YupResolver

  • повторные пересоздания схемы вызывают лишние вычисления
  • рекомендуется мемоизация через useMemo
  • асинхронные тесты увеличивают latency submit
  • большие схемы могут влиять на render performance при частой валидации

Оптимизация производительности после миграции

const schema = useMemo(() => yup.object({...}), []);

  • уменьшение числа test()
  • перенос тяжелых проверок на backend
  • использование abortEarly: true для быстрых форм

Сопоставление ключевых особенностей библиотек

Функция Zod Joi Custom Yup
Strict typing +++ + - +
Frontend integration ++ + + +++
Async validation ++ + +++ +++
Schema readability ++ + + +++

Итоговая структура перехода на YupResolver

  • анализ существующих схем
  • выделение типов и зависимостей
  • перенос базовых правил
  • реализация кастомных тестов
  • унификация ошибок через resolver
  • оптимизация производительности
  • стабилизация поведения формы