Контекст и this в валидаторах

В библиотеке Yup контекст валидаторов строится вокруг функции test, в которой доступен специальный объект исполнения. Этот объект объединяет сведения о текущем значении, схеме, родительских данных и пользовательском контексте, переданном при валидации.

Контекст в Yup не является глобальным состоянием. Он формируется заново при каждом вызове .validate() или .isValid() и привязывается к конкретному запуску проверки схемы.

Ключевая особенность: this внутри пользовательских валидаторов зависит от типа функции. Обычные функции получают привязанный контекст, стрелочные функции — нет.


this внутри test()

Метод .test() является основным инструментом для создания пользовательских проверок:

Yup.string().test('custom-test', 'Ошибка валидации', function (value) {
  return value === 'ok';
});

Внутри такой функции доступен контекст через this:

  • this.value — текущее значение
  • this.originalValue — исходное значение до преобразований
  • this.path — путь поля в объекте
  • this.schema — текущая схема
  • this.createError() — создание кастомной ошибки
  • this.options — параметры валидации

Пример использования:

Yup.number().test('positive-check', function (value) {
  if (value <= 0) {
    return this.createError({
      message: `Поле ${this.path} должно быть положительным числом`
    });
  }
  return true;
});

Важность обычных функций

Контекст this работает только в обычных функциях. При использовании стрелочных функций контекст теряется:

// НЕПРАВИЛЬНО
Yup.number().test('fail', (value) => {
  console.log(this.path); // undefined
  return value > 0;
});
// ПРАВИЛЬНО
Yup.number().test('ok', function (value) {
  console.log(this.path); // работает корректно
  return value > 0;
});

Причина заключается в том, что стрелочные функции не имеют собственного this и наследуют его из внешнего окружения, которое не связано с механизмом Yup.


Контекст через options.context

При вызове валидации можно передавать внешний контекст:

schema.validate(data, {
  context: {
    role: 'admin'
  }
});

Этот объект становится доступен внутри тестов:

Yup.string().test('role-check', function (value) {
  if (this.options.context.role !== 'admin') {
    return this.createError({ message: 'Недостаточно прав' });
  }
  return true;
});

Контекст передаётся только в момент выполнения и не хранится в самой схеме.


this.parent и работа с вложенными объектами

При валидации объектов особое значение имеет this.parent, содержащий данные текущего уровня:

Yup.object({
  password: Yup.string(),
  confirm: Yup.string().test('match', function (value) {
    return value === this.parent.password;
  })
});

this.parent позволяет обращаться к соседним полям объекта без необходимости дублирования значений.


this.schema, this.path и служебные поля

Контекст Yup предоставляет доступ к метаданным схемы:

this.schema

Текущий узел схемы, полезен для динамического анализа правил:

this.schema.type

this.path

Путь до поля в объекте:

'user.profile.email'

Используется при формировании сообщений об ошибках.

this.type

Тип значения схемы (string, number, array, object).


Создание кастомных ошибок через this.createError

Механизм ошибок в Yup централизован через createError, который позволяет задавать не только сообщение, но и путь:

Yup.number().test('range', function (value) {
  if (value > 100) {
    return this.createError({
      path: this.path,
      message: 'Значение превышает допустимый диапазон'
    });
  }
  return true;
});

Такой подход обеспечивает точное позиционирование ошибки в структуре данных.


Доступ к параметрам теста через this.options

Объект this.options содержит параметры текущей валидации:

  • abortEarly
  • context
  • stripUnknown
  • recursive

Пример:

Yup.string().test('debug', function (value) {
  if (this.options.abortEarly) {
    // поведение при раннем завершении
  }
  return true;
});

Асинхронный контекст и промисы

Контекст сохраняется и в асинхронных тестах:

Yup.string().test('async-check', async function (value) {
  const role = this.options.context.role;

  const allowed = await checkPermission(role);
  if (!allowed) {
    return this.createError({ message: 'Запрещено' });
  }

  return true;
});

Важно, что this остаётся доступным даже при async/await.


Типовые ошибки при работе с контекстом

Потеря this из-за стрелочных функций

Самая частая проблема — использование стрелочных функций в .test().

Попытка доступа к context вне validate

Контекст существует только во время выполнения .validate(). При обращении к schema напрямую он отсутствует.

Неправильное ожидание глобальности context

context не является глобальным состоянием схемы и не сохраняется между вызовами.


Комбинирование контекста и родительских данных

Контекст и this.parent часто используются вместе:

Yup.object({
  role: Yup.string(),
  limit: Yup.number().test('limit-check', function (value) {
    const role = this.parent.role;
    const max = this.options.context.maxLimit;

    return role === 'admin' || value <= max;
  })
});

Такой подход позволяет объединять данные формы и внешние параметры системы.


Поведение контекста в цепочках схем

Вложенные схемы сохраняют доступ к общему context, но parent меняется на каждом уровне вложенности. Это позволяет строить изолированные проверки при сохранении глобальных параметров исполнения.