Изменения в API между версиями

В ранних версиях Yup основная модель построения схем опиралась на цепочечные вызовы через фабрики типов: string(), number(), boolean(), object(), array(). С течением времени API стал более строгим и предсказуемым, а поведение некоторых методов изменилось для устранения неоднозначностей.

Одним из ключевых изменений стало постепенное выравнивание поведения базового класса Schema. В более старых версиях методы вроде required(), nullable(), default() могли вести себя по-разному в зависимости от типа схемы. В новых версиях их поведение унифицировано: теперь они наследуются от общего прототипа и обрабатываются консистентно независимо от типа данных.

Особое внимание уделялось работе с mixed() — базовым типом, от которого наследуются все остальные схемы. Ранее он использовался редко, но позже стал центральной точкой расширения через addMethod, что привело к изменению внутренней архитектуры расширяемости.

Изменения в механизме валидации

Одним из наиболее заметных изменений стало поведение методов validate, isValid и validateSync.

В старых версиях:

  • validate() мог возвращать не всегда предсказуемые ошибки при использовании abortEarly
  • isValid() иногда запускал полную валидацию даже при частичном успехе
  • синхронные и асинхронные пути выполнения имели больше различий

В более новых версиях:

  • validate() стал строго Promise-ориентированным
  • validateSync() получил более ограниченную область применения и явное поведение при ошибках
  • abortEarly по умолчанию изменил семантику остановки: теперь он предсказуемо останавливает проверку на первой ошибке без частичного накопления состояния

Также была стабилизирована работа с ValidationError, который стал более структурированным: поле inner теперь гарантированно содержит массив ошибок только при отключённом abortEarly.

Изменения в объектных схемах

Существенная переработка затронула object() и работу с вложенными схемами.

Ранее:

  • доступ к вложенным полям через reach(schema, path) был менее строгим
  • поведение при отсутствии ключей в объекте зависело от strict и noUnknown

Позже:

  • reach стал строго типизированным инструментом доступа к схеме
  • поведение noUnknown унифицировалось и перестало влиять на валидацию вложенных схем
  • добавилась более предсказуемая обработка default() для вложенных объектов

Изменение архитектуры привело к тому, что схемы объектов стали ближе к декларативному описанию структуры данных, а не к динамической проверке в рантайме.

Типизация и интеграция с TypeScript

Одним из крупнейших изменений между версиями стала эволюция типизации.

В ранних версиях:

  • типы были частично экспериментальными
  • InferType часто давал неточные результаты
  • цепочки .required().nullable() могли приводить к конфликтам типов

Позже:

  • улучшена поддержка условных типов
  • добавлена более точная инференция для object().shape()
  • исправлены проблемы с union-типацией в mixed()

Особенно заметно изменение поведения при использовании as const-подходов: схемы стали лучше выводить literal-типы, что снизило необходимость ручного указания generic-параметров.

Изменения в кастомных валидаторах

Метод test() подвергся значительной переработке.

Ранее:

  • функция теста могла возвращать как boolean, так и исключение
  • асинхронные тесты не всегда корректно обрабатывались

Позже:

  • строго закреплено правило возврата true | false | ValidationError
  • асинхронные тесты стали полностью Promise-совместимыми
  • улучшена обработка контекста через this

Изменилось и поведение addMethod(). Если ранее добавленные методы могли конфликтовать при повторном объявлении, то позже введена более строгая регистрация, предотвращающая перезапись без явного намерения.

Изменения в работе с трансформациями

Метод transform() стал одним из ключевых инструментов нормализации данных, и его поведение также менялось.

Ранее трансформации:

  • применялись не всегда последовательно
  • могли конфликтовать с default()
  • иногда выполнялись после валидации

В более поздних версиях:

  • порядок выполнения строго зафиксирован: сначала cast, затем transform, затем validate
  • устранены конфликты между transform и nullable
  • добавлена более предсказуемая работа с NaN, undefined и пустыми строками

Это изменение сделало схемы более детерминированными при обработке “грязных” данных из форм и API.

Условные схемы и when()

Метод when() претерпел значительную переработку логики зависимостей.

Ранее:

  • условия могли вычисляться неочевидно при глубокой вложенности
  • зависимости не всегда отслеживались реактивно

Позже:

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

Также изменилось поведение при использовании switch-подобной логики: теперь каждая ветка вычисляется детерминированно, без побочных эффектов от предыдущих условий.

Работа с массивами и вложенными структурами

Схемы array() получили ряд изменений в API и поведении:

Ранее:

  • of() мог вести себя непредсказуемо при смешанных типах
  • проверка вложенных элементов не всегда корректно агрегировала ошибки

Позже:

  • усилена строгая проверка элементов массива
  • улучшена агрегация ValidationError.inner
  • добавлена предсказуемая работа с пустыми массивами и undefined

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

Изменения в обработке ошибок

Класс ValidationError стал более формализованным.

Ранее:

  • структура ошибок могла отличаться в зависимости от источника
  • поле path иногда отсутствовало

Позже:

  • гарантированное наличие path, message и type
  • унифицирован формат inner
  • добавлена более строгая сериализация ошибок

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

Удалённые и устаревшие элементы API

В процессе развития библиотеки ряд методов был признан устаревшим:

  • частично изменено поведение concat() в сторону более строгого слияния схем
  • ограничено использование неявных преобразований типов внутри mixed()
  • устаревшие паттерны работы с cast постепенно заменены на явные трансформации

Удаление старого поведения происходило постепенно, через предупреждения и обратную совместимость, но в новых версиях часть старых возможностей полностью исключена ради консистентности API.

Стабилизация цепочек вызовов

Цепочный API, являющийся основой Yup, также был пересмотрен.

Ранее:

  • порядок вызовов иногда влиял на результат непредсказуемо
  • комбинации nullable, required, default могли конфликтовать

Позже:

  • определён строгий порядок применения модификаторов схемы
  • устранены побочные эффекты от перестановки методов
  • улучшена предсказуемость при повторном использовании схем

Теперь каждая цепочка воспринимается как декларативное описание, а не как последовательность императивных изменений.

Поведение по умолчанию и совместимость

Одним из наиболее критичных изменений стало изменение дефолтных настроек:

  • abortEarly получил более строгую семантику
  • поведение strict стало более последовательным
  • некоторые неявные преобразования типов отключены по умолчанию

Это привело к необходимости пересмотра многих старых схем, особенно тех, что рассчитывали на мягкую типизацию входных данных.

Совместимость между версиями поддерживается частично, но логика построения схем требует более явного описания намерений разработчика, чем в ранних релизах библиотеки.