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

Приведение типов в Yup основано на механизме кастинга (casting), который выполняется до этапа валидации и определяет, в какой форме данные будут интерпретироваться внутри схемы. Это поведение делает библиотеку удобной для работы с формами и внешними источниками данных, где значения часто приходят в виде строк.

Каждая схема в Yup не только проверяет данные, но и приводит их к ожидаемому типу. Это происходит через внутренний метод cast(), который применяется:

  • при явном вызове schema.cast(value)
  • автоматически перед валидацией (validate)
  • при использовании значений по умолчанию

Важно понимать, что приведение типов и валидация — это разные этапы. Сначала выполняется преобразование, затем проверка ограничений.

Поведение по умолчанию (strict mode)

По умолчанию Yup работает в нестрогом режиме:

schema.validate(value, { strict: false })

В этом режиме происходит автоматическое приведение типов:

  • строки "123" преобразуются в число 123
  • "true" и "false" могут интерпретироваться как булевы значения (в зависимости от схемы)
  • строки дат преобразуются в объекты Date

Если включить строгий режим:

schema.validate(value, { strict: true })

приведение отключается, и данные проверяются «как есть».

Числовые схемы и приведение строк

Наиболее распространённый случай — преобразование строк в числа:

import * as yup from 'yup';

const schema = yup.number();

schema.cast("42"); // 42 (number)
schema.cast("42px"); // NaN
schema.cast(""); // NaN

Особенности поведения:

  • пустая строка "" преобразуется в NaN
  • некорректные числовые строки также дают NaN
  • дальнейшая валидация может использовать .required(), .positive(), .integer() и другие правила

Пример с валидацией:

const schema = yup.number().required().positive();

schema.validate("10"); // 10
schema.validate("-5"); // ошибка (negative)

Строковые схемы и нормализация значений

Строковые схемы обычно не требуют сложного приведения, но Yup всё равно нормализует входные данные:

const schema = yup.string();

schema.cast(123); // "123"
schema.cast(null); // "null" (если не nullable)
schema.cast(undefined); // undefined

Особенность: любые примитивы приводятся к строке через стандартное String(value).

Для ограничения поведения используется:

yup.string().nullable();
yup.string().strict(true);

Булевы значения и неоднозначность входных данных

Булев тип является одним из самых неоднозначных:

const schema = yup.boolean();

schema.cast("true");  // true
schema.cast("false"); // false
schema.cast(1);       // true
schema.cast(0);       // false

Поведение основано на внутренних правилах интерпретации «truthy/falsy» значений. Однако важно учитывать, что строковые значения обрабатываются явно, а не через JavaScript-приведение.

Даты и автоматическое создание объектов Date

При использовании yup.date() происходит попытка создания объекта Date:

const schema = yup.date();

schema.cast("2024-01-01"); // Date объект
schema.cast(1704067200000); // Date из timestamp

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

schema.cast("invalid date"); // Invalid Date

Дальнейшая валидация через .typeError() или .min() / .max() позволяет контролировать корректность.

Механизм transform

Ключевым инструментом управления приведением типов является метод transform:

const schema = yup.number().transform((value, originalValue) => {
  if (typeof originalValue === "string") {
    return originalValue.trim() === "" ? undefined : Number(originalValue);
  }
  return value;
});

Функция transform получает:

  • value — уже приведённое значение
  • originalValue — исходное значение до кастинга

Этот механизм позволяет:

  • фильтровать пустые строки
  • реализовывать пользовательскую логику приведения
  • предотвращать нежелательные преобразования

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

При вызове:

schema.validate(value)

выполняется последовательность:

  1. cast(value)
  2. transform (если задан)
  3. проверка правил (required, min, max, и т.д.)

Прямой вызов:

schema.cast(value)

выполняет только этап приведения без валидации.

Значения по умолчанию и влияние на типы

При использовании .default() происходит приведение значения по той же логике:

const schema = yup.number().default("10");

schema.cast(undefined); // 10 (number)

Если значение по умолчанию несовместимо с типом, оно также проходит кастинг.

Nullable и влияние на приведение

Схема:

yup.string().nullable()

позволяет сохранить null без преобразования:

schema.cast(null); // null

Без nullable() значение может быть преобразовано в строку "null".

Глубокое приведение объектов и массивов

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

const schema = yup.object({
  age: yup.number(),
  name: yup.string()
});

schema.cast({
  age: "25",
  name: 123
});

Результат:

{
  age: 25,
  name: "123"
}

Для массивов:

const schema = yup.array().of(yup.number());

schema.cast(["1", "2", "3"]); // [1, 2, 3]

Ограничения и потенциальные проблемы приведения

Автоматическое приведение может приводить к неоднозначным ситуациям:

  • пустые строки становятся NaN в числах
  • "false" всегда интерпретируется как false
  • некорректные даты превращаются в Invalid Date, но не выбрасывают ошибку сразу
  • неожиданные строки могут быть преобразованы в значения, проходящие валидацию

Поэтому важно контролировать поведение через:

  • strict(true)
  • transform()
  • явные .typeError()

Приоритеты преобразований

Порядок обработки значения внутри схемы:

  1. входное значение
  2. cast
  3. transform
  4. применение defaults (если необходимо)
  5. валидация правил

Этот порядок определяет итоговое значение, которое возвращается после validate.

Поведение при отсутствии значения

undefined обрабатывается отдельно:

  • может быть заменён значением .default()
  • может оставаться undefined, если не задано обязательное поле
  • участвует в логике .required()
yup.number().required().validate(undefined); // ошибка

Итоговая модель преобразования

Механизм приведения типов в Yup строится на комбинации:

  • автоматического кастинга
  • пользовательских трансформаций
  • строгого режима
  • схемных правил (number, string, date и др.)

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