Trim и удаление пробелов

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

В Yup строковые схемы создаются через Yup.string(). Все преобразования значения происходят на этапе трансформации, до выполнения проверок required, min, max, matches и других правил.

Ключевой момент: Yup сначала нормализует данные, затем валидирует их.

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


Метод trim(): удаление пробелов по краям строки

Основной инструмент для очистки строк — встроенный метод .trim().

import * as Yup from 'yup';

const schema = Yup.object({
  username: Yup.string()
    .trim()
    .required('Имя пользователя обязательно'),
});

Поведение trim()

Метод .trim() удаляет:

  • пробелы в начале строки
  • пробелы в конце строки
  • табуляции по краям
  • переносы строк по краям

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

"   hello world   " → "hello world"
"\n\tuser\t\n" → "user"

Важно, что внутренние пробелы не изменяются:

"john   doe" → "john   doe"

Влияние trim() на required()

Одно из частых заблуждений связано с тем, как .trim() влияет на required().

const schema = Yup.string()
  .trim()
  .required('Обязательное поле');

Если пользователь вводит:

"     "

После .trim() строка становится:

""

И уже пустая строка считается невалидной для required().

Таким образом, .trim() усиливает строгость проверки обязательных полей, исключая случаи ввода “псевдозначений” из пробелов.


trim() и nullable / optional значения

Поведение изменяется при использовании nullable() и notRequired().

const schema = Yup.string()
  .trim()
  .nullable()
  .notRequired();

В этом случае:

  • null допустим
  • undefined допустим
  • пустая строка после trim остаётся пустой строкой

Важно различать null и "", так как .trim() не преобразует null.


Использование transform для расширенной очистки

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

Yup позволяет использовать .transform():

const schema = Yup.string()
  .transform((value) => {
    if (typeof value !== 'string') return value;
    return value.trim().replace(/\s+/g, ' ');
  });

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

  • trim() — удаление внешних пробелов
  • replace(/\s+/g, ' ') — замена множественных пробелов одним

Полное удаление всех пробелов

Иногда требуется убрать все пробелы, включая внутренние.

const schema = Yup.string()
  .transform((value) => {
    if (typeof value !== 'string') return value;
    return value.replace(/\s/g, '');
  });

Результаты:

" a b c " → "abc"
"john doe" → "johndoe"

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


Влияние порядка вызова методов

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

Yup.string()
  .trim()
  .min(3);

или

Yup.string()
  .min(3)
  .trim();

Фактически корректным является первый вариант, поскольку:

  • .trim() должен очистить данные до проверки длины
  • иначе пробелы могут влиять на min

Пример:

"  abc  "
  • без trim длина = 7
  • с trim длина = 3

Поведение с Unicode-пробелами

Метод .trim() учитывает стандартные пробельные символы Unicode:

  • обычный пробел
  • non-breaking space (\u00A0)
  • табуляции
  • переносы строк

Однако специфические символы пробела могут вести себя по-разному в зависимости от среды выполнения JavaScript.


Комбинирование trim с другими валидаторами

На практике .trim() часто используется вместе с ограничениями длины и формата:

const schema = Yup.string()
  .trim()
  .min(2)
  .max(30)
  .matches(/^[a-zA-Zа-яА-Я]+$/, 'Только буквы');

Здесь порядок критичен: сначала нормализация, затем проверки.


Частые ошибки при работе с пробелами

Проверка длины без trim

Yup.string().min(3)

Проблема: " a " проходит как длина 3, хотя фактическое значение — один символ.


Использование transform без проверки типа

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

Ошибка возникает, если значение undefined или null.

Корректный вариант:

.transform((value) => {
  if (typeof value !== 'string') return value;
  return value.trim();
});

Двойная нормализация

Часто встречается ситуация, когда данные уже очищаются на уровне UI, а затем повторно через Yup. Это приводит к дублирующей обработке, которая не всегда очевидна при отладке.


Практический паттерн очистки строк

Для большинства форм используется стандартная схема:

const cleanString = Yup.string()
  .transform((value) => {
    if (typeof value !== 'string') return value;
    return value.trim().replace(/\s+/g, ' ');
  })
  .trim()
  .required();

Такой подход обеспечивает:

  • удаление внешних пробелов
  • нормализацию внутренних пробелов
  • корректную работу required
  • предсказуемый результат валидации

Особенности работы в реальных формах

При интеграции с формами (например, React Hook Form или Formik) важно учитывать, что Yup может изменять значение до отправки:

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

Это особенно важно для полей логинов, email и идентификаторов, где пробелы считаются критичными.


Влияние trim на сравнение значений

При использовании .oneOf() или .equals() предварительная очистка может изменить результат сравнения:

Yup.string()
  .trim()
  .oneOf(['admin']);

В этом случае:

" admin " → "admin"

что приводит к успешному совпадению, даже если пользователь ввёл лишние пробелы.


Итоговая логика обработки пробелов в Yup

Валидация строк в Yup строится на последовательной модели:

  1. Преобразование значения (transform, trim)
  2. Нормализация строки
  3. Применение ограничений (min, max, matches, required)
  4. Формирование результата валидации

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