Преобразование после валидации

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

Преобразование позволяет:

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

Основным инструментом является метод transform().


Общая схема работы Yup

При обработке значения Yup выполняет несколько этапов:

  1. Получение исходного значения.
  2. Преобразование (transform).
  3. Приведение типов (cast).
  4. Валидация (validate).
  5. Возврат итогового результата.

Пример:

import * as yup from 'yup';

const schema = yup.string().transform((value) => {
  return value.trim();
});

const result = await schema.validate('   hello   ');

console.log(result);

Результат:

hello

Исходная строка была преобразована до проверки результата.


Метод transform()

Сигнатура

transform((currentValue, originalValue) => {})

Параметры:

Параметр Описание
currentValue Текущее значение после предыдущих преобразований
originalValue Исходное значение до любых изменений

Пример:

const schema = yup.string().transform((value, originalValue) => {
  console.log(value);
  console.log(originalValue);

  return value;
});

Простейшее преобразование строки

Удаление пробелов:

const schema = yup.string().transform((value) => {
  return value.trim();
});

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

const result = await schema.validate('   admin   ');

console.log(result);

Результат:

admin

Преобразование регистра

Нижний регистр

const schema = yup.string().transform((value) => {
  return value.toLowerCase();
});

Верхний регистр

const schema = yup.string().transform((value) => {
  return value.toUpperCase();
});

Пример:

const result = await schema.validate('JavaScript');

console.log(result);

Результат:

JAVASCRIPT

Несколько transform подряд

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

const schema = yup
  .string()
  .transform((value) => value.trim())
  .transform((value) => value.toLowerCase())
  .transform((value) => value.replace(/\s+/g, '-'));

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

const result = await schema.validate('   Hello World   ');

console.log(result);

Результат:

hello-world

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

originalValue содержит исходные данные до любых преобразований.

const schema = yup.string().transform((value, originalValue) => {
  console.log('Текущее:', value);
  console.log('Исходное:', originalValue);

  return value.trim();
});

Преобразование пустых строк в null

Одна из самых распространённых задач.

const schema = yup.string().transform((value) => {
  return value === '' ? null : value;
}).nullable();

Пример:

const result = await schema.validate('');

console.log(result);

Результат:

null

Преобразование чисел

Конвертация строки в число

const schema = yup.number().transform((value, originalValue) => {
  return Number(originalValue);
});

Пример:

const result = await schema.validate('42');

console.log(result);

Результат:

42

Обработка NaN

const schema = yup.number().transform((value, originalValue) => {
  const parsed = Number(originalValue);

  return isNaN(parsed) ? undefined : parsed;
});

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

const schema = yup.date().transform((value, originalValue) => {
  return new Date(originalValue);
});

Автоматическая очистка телефона

const schema = yup.string().transform((value) => {
  return value.replace(/\D/g, '');
});

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

const result = await schema.validate('+7 (777) 123-45-67');

console.log(result);

Результат:

77771234567

Форматирование email

const schema = yup
  .string()
  .email()
  .transform((value) => value.trim().toLowerCase());

Преобразование массивов

Очистка элементов массива

const schema = yup.array().transform((value) => {
  return value.map(item => item.trim());
});

Пример:

const result = await schema.validate([
  ' one ',
  ' two ',
  ' three '
]);

console.log(result);

Результат:

['one', 'two', 'three']

Удаление пустых элементов массива

const schema = yup.array().transform((value) => {
  return value.filter(Boolean);
});

Уникализация массива

const schema = yup.array().transform((value) => {
  return [...new Set(value)];
});

Преобразование объектов

Очистка строковых полей

const schema = yup.object({
  name: yup.string(),
  city: yup.string()
}).transform((value) => {
  return {
    ...value,
    name: value.name?.trim(),
    city: value.city?.trim()
  };
});

Переименование полей

const schema = yup.object().transform((value) => {
  return {
    firstName: value.name,
    age: value.age
  };
});

Удаление лишних полей

const schema = yup.object().transform((value) => {
  return {
    id: value.id,
    email: value.email
  };
});

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

const schema = yup.object({
  profile: yup.object({
    firstName: yup.string(),
    lastName: yup.string()
  })
}).transform((value) => {
  return {
    ...value,
    profile: {
      ...value.profile,
      firstName: value.profile.firstName.trim(),
      lastName: value.profile.lastName.trim()
    }
  };
});

Метод cast()

cast() выполняет преобразование без валидации.

const schema = yup.number();

const result = schema.cast('25');

console.log(result);

Результат:

25

Разница между cast() и validate()

cast()

  • только преобразует данные;
  • не выбрасывает ошибки валидации;
  • используется для подготовки данных.

validate()

  • выполняет преобразование;
  • выполняет проверку;
  • выбрасывает ошибки.

strict() и отключение преобразований

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

const schema = yup.number();

await schema.validate('42');

Результат:

42

При использовании strict() автоматическое преобразование отключается.

const schema = yup.number().strict();

await schema.validate('42');

Ошибка:

this must be a `number` type

Влияние strict() на transform()

transform() не применяется в strict-режиме.

const schema = yup
  .string()
  .strict()
  .transform(value => value.trim());

Использование default()

Преобразование часто комбинируется со значениями по умолчанию.

const schema = yup.string()
  .transform(value => value?.trim())
  .default('guest');

Комбинация nullable() и transform()

const schema = yup
  .string()
  .nullable()
  .transform(value => value === '' ? null : value);

Преобразование boolean

Строка в boolean

const schema = yup.boolean().transform((value, originalValue) => {
  if (originalValue === 'true') {
    return true;
  }

  if (originalValue === 'false') {
    return false;
  }

  return value;
});

Нормализация данных формы

Типичный пример для frontend-приложений.

const schema = yup.object({
  name: yup.string()
    .transform(v => v.trim()),

  email: yup.string()
    .transform(v => v.trim().toLowerCase()),

  age: yup.number(),

  phone: yup.string()
    .transform(v => v.replace(/\D/g, ''))
});

Очистка HTML

const schema = yup.string().transform((value) => {
  return value.replace(/<[^>]*>?/gm, '');
});

Преобразование slug

const schema = yup.string().transform((value) => {
  return value
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/[^\w-]+/g, '');
});

Пример:

const result = await schema.validate('  Hello JavaScript World!  ');

console.log(result);

Результат:

hello-javascript-world

Использование transform с when()

const schema = yup.object({
  type: yup.string(),

  value: yup.string().when('type', {
    is: 'email',
    then: schema =>
      schema.transform(v => v.toLowerCase()),

    otherwise: schema =>
      schema.transform(v => v.trim())
  })
});

Защита от undefined

Частая ошибка при преобразовании — вызов методов строки у undefined.

Небезопасный вариант:

.transform(value => value.trim())

Безопасный вариант:

.transform(value => value?.trim())

Преобразование перед сериализацией

const schema = yup.object({
  username: yup.string()
    .transform(v => v.trim().toLowerCase()),

  tags: yup.array()
    .transform(v => [...new Set(v)])
});

Комплексный пример

import * as yup from 'yup';

const userSchema = yup.object({
  name: yup.string()
    .required()
    .transform(v => v?.trim()),

  email: yup.string()
    .email()
    .required()
    .transform(v => v?.trim().toLowerCase()),

  age: yup.number()
    .transform((v, original) => {
      const parsed = Number(original);

      return isNaN(parsed) ? undefined : parsed;
    }),

  phone: yup.string()
    .transform(v => v?.replace(/\D/g, '')),

  website: yup.string()
    .transform(v => v === '' ? null : v)
    .nullable(),

  tags: yup.array()
    .transform(v => [...new Set(v)])
});

const result = await userSchema.validate({
  name: '   Alex   ',
  email: '  ADMIN@MAIL.COM ',
  age: '25',
  phone: '+7 (777) 123-45-67',
  website: '',
  tags: ['js', 'js', 'node']
});

console.log(result);

Результат:

{
  name: 'Alex',
  email: 'admin@mail.com',
  age: 25,
  phone: '77771234567',
  website: null,
  tags: ['js', 'node']
}

Порядок выполнения преобразований

Последовательность имеет значение.

const schema = yup.string()
  .transform(v => v.trim())
  .transform(v => v.toLowerCase());

Сначала:

"  Hello "

станет:

"Hello"

затем:

"hello"

Типичные ошибки

Возврат undefined без необходимости

.transform(() => undefined)

Может приводить к неожиданным ошибкам required-полей.


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

.transform(() => 'text')

для объекта:

yup.object()

Нарушает ожидаемую структуру схемы.


Мутация исходного объекта

Плохой вариант:

.transform(value => {
  value.name = value.name.trim();
  return value;
})

Безопасный вариант:

.transform(value => ({
  ...value,
  name: value.name.trim()
}))

Производительность transform()

Трансформации выполняются при каждой валидации.

Нежелательно:

  • выполнять тяжёлые вычисления;
  • обращаться к внешним API;
  • использовать сложные синхронные операции;
  • создавать глубокие копии больших структур без необходимости.

Практические сценарии использования

Backend API

  • очистка входящих данных;
  • нормализация payload;
  • приведение типов;
  • удаление мусорных символов.

Frontend-формы

  • форматирование телефона;
  • очистка email;
  • удаление пробелов;
  • преобразование пустых строк.

Административные панели

  • нормализация slug;
  • приведение идентификаторов;
  • подготовка данных перед отправкой.

Работа с базой данных

  • конвертация дат;
  • преобразование nullable-полей;
  • очистка сериализуемых структур.