Плагины для Yup

Библиотека Yup построена вокруг концепции схемной валидации, где базовые типы (string, number, object, array) предоставляют ядро, а расширение поведения достигается через модификацию прототипов схем и добавление пользовательских методов. Такой подход позволяет формировать слой переиспользуемых правил, который в практике разработки часто называют «плагинной системой», хотя формально отдельного API для плагинов в Yup нет.

Расширения реализуются через несколько механизмов:

  • addMethod — добавление собственных методов к схемам
  • test — регистрация пользовательских валидаторов
  • transform — предобработка входных данных
  • композиция схем через mixed и обобщённые типы

Расширение через addMethod

Основной способ создания плагинов — добавление методов в прототипы схем. Это позволяет внедрять доменные правила в виде цепочечных вызовов.

import * as Yup from "yup";

Yup.addMethod(Yup.string, "onlyDigits", function (message) {
  return this.test("onlyDigits", message, function (value) {
    if (value == null) return true;
    return /^\d+$/.test(value);
  });
});

Использование:

const schema = Yup.object({
  phone: Yup.string().onlyDigits("Допустимы только цифры"),
});

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

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

Наиболее частый сценарий — расширение строковых схем для нормализации данных и проверки бизнес-ограничений.

Yup.addMethod(Yup.string, "trimmed", function () {
  return this.transform((value) =>
    typeof value === "string" ? value.trim() : value
  );
});
Yup.addMethod(Yup.string, "noSpaces", function (message) {
  return this.test("noSpaces", message, (value) =>
    value ? !/\s/.test(value) : true
  );
});

Комбинирование таких методов формирует декларативные цепочки:

const schema = Yup.object({
  username: Yup.string().trimmed().noSpaces(),
});

Плагины для числовых схем

Числовые расширения часто используются для доменной валидации: диапазоны, шаги, ограничения финансовых значений.

Yup.addMethod(Yup.number, "isMultipleOf", function (factor, message) {
  return this.test("isMultipleOf", message, (value) => {
    if (value == null) return true;
    return value % factor === 0;
  });
});
const schema = Yup.object({
  quantity: Yup.number().isMultipleOf(5, "Кратно пяти"),
});

Пользовательские валидаторы через test

Метод test является ядром плагинной архитектуры. Он позволяет создавать динамические проверки с доступом к контексту схемы и родительским значениям.

Yup.addMethod(Yup.string, "startsWithUpper", function (message) {
  return this.test("startsWithUpper", message, function (value) {
    if (!value) return true;
    return /^[A-ZА-Я]/.test(value);
  });
});

Более сложные сценарии используют this.parent:

Yup.addMethod(Yup.string, "matchField", function (fieldName, message) {
  return this.test("matchField", message, function (value) {
    return value === this.parent[fieldName];
  });
});

Transform как часть плагинной системы

Механизм transform позволяет вмешиваться в данные до выполнения валидации. Это часто используется для нормализации входа.

Yup.addMethod(Yup.string, "toLower", function () {
  return this.transform((value) =>
    typeof value === "string" ? value.toLowerCase() : value
  );
});

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

Плагины для object-схем

Object-схемы применяются для агрегации логики и условной валидации. Плагины здесь часто работают как генераторы структурных правил.

Yup.addMethod(Yup.object, "atLeastOne", function (fields, message) {
  return this.test("atLeastOne", message, function (value) {
    if (!value) return false;
    return fields.some((field) => Boolean(value[field]));
  });
});

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

const schema = Yup.object({
  email: Yup.string(),
  phone: Yup.string(),
}).atLeastOne(["email", "phone"], "Нужно заполнить хотя бы одно поле");

Композиция плагинов

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

const schema = Yup.object({
  password: Yup.string()
    .trimmed()
    .min(8)
    .noSpaces()
    .startsWithUpper("Первая буква должна быть заглавной"),
});

Порядок применения методов имеет значение, поскольку transform выполняется до test, а валидаторы выполняются последовательно.

Переиспользуемые модули расширений

Практика выделения плагинов в отдельные модули позволяет формировать слой доменной валидации.

// validators/stringPlugins.js
export function applyStringPlugins(Yup) {
  Yup.addMethod(Yup.string, "slug", function () {
    return this.transform((value) =>
      typeof value === "string"
        ? value.toLowerCase().replace(/\s+/g, "-")
        : value
    );
  });
}
import * as Yup from "yup";
import { applyStringPlugins } from "./validators/stringPlugins";

applyStringPlugins(Yup);

Условные плагины и контекст схем

Использование when в сочетании с кастомными методами расширяет поведение плагинов до контекстной логики.

Yup.addMethod(Yup.string, "requiredIf", function (field, value) {
  return this.when(field, {
    is: value,
    then: (schema) => schema.required(),
  });
});

Плагины и mixed-схемы

mixed используется как универсальная точка расширения, когда тип данных заранее неизвестен.

Yup.addMethod(Yup.mixed, "notNullOrUndefined", function (message) {
  return this.test("notNullOrUndefined", message, (value) =>
    value !== null && value !== undefined
  );
});

Взаимодействие с TypeScript

При использовании Yup в TypeScript требуется расширение деклараций типов для сохранения автокомплита.

declare module "yup" {
  interface StringSchema {
    onlyDigits(message?: string): this;
    trimmed(): this;
  }
}

Без декларативного расширения типизация теряет информацию о добавленных методах.

Организация архитектуры плагинов

Плагины группируются по функциональным областям:

  • нормализация данных (trim, lowerCase, slug)
  • бизнес-валидация (domain rules)
  • межполевая проверка (cross-field validation)
  • форматирование и трансформация входных значений

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

Ограничения плагинного подхода

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

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

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