String

Валидация строк в Superstruct строится вокруг базового структурного типа string(), который задаёт фундаментальное правило: значение должно быть строкой JavaScript. Любые дальнейшие ограничения накладываются поверх этого базового инварианта через композицию функций.

Базовая структура:

import { string } from "superstruct";

const Name = string();

На этом уровне структура допускает любое строковое значение, включая пустую строку и строки с пробелами. Все дополнительные правила вводятся отдельно и не являются частью базовой проверки.


Базовая проверка типа строки

Основная ответственность string() — гарантировать, что значение соответствует примитивному типу string.

Примеры допустимых значений:

  • "hello"
  • ""
  • "123"
  • "строка с пробелами"

Недопустимые значения:

  • 123
  • true
  • null
  • undefined
  • {}

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


Ограничение длины строки

Для управления длиной строки используется функция size, которая позволяет задать минимальную и максимальную границу.

import { string, size } from "superstruct";

const Username = size(string(), 3, 20);

Здесь накладываются правила:

  • минимальная длина — 3 символа
  • максимальная длина — 20 символов

Поведение при нарушении:

  • строка короче минимальной длины → ошибка валидации
  • строка длиннее максимальной → ошибка валидации

Использование size удобно в случаях, когда требуется единое ограничение диапазона, например для логинов или кодов подтверждения.


Проверка на пустую строку

Частый случай — запрет пустых строк. В Superstruct это реализуется через refine.

import { string, refine } from "superstruct";

const NonEmptyString = refine(string(), "NonEmptyString", (value) => {
  return value.length > 0;
});

Функция refine позволяет добавить произвольную бизнес-логику поверх базового типа.

Ключевые особенности:

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

Расширенный вариант часто включает также проверку пробельных строк:

const StrictNonEmptyString = refine(string(), "StrictNonEmptyString", (value) => {
  return value.trim().length > 0;
});

Использование регулярных выражений

Для формальной валидации форматов применяется pattern.

import { string, pattern } from "superstruct";

const Email = pattern(
  string(),
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/
);

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

Типичные сценарии:

  • email
  • телефонные номера
  • идентификаторы
  • коды продуктов
  • UUID

Пример UUID:

const UUID = pattern(
  string(),
  /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
);

Трансформация строк через coerce

В реальных приложениях часто требуется привести входные данные к строке перед валидацией. Для этого используется coerce.

import { string, coerce } from "superstruct";

const StringFromAny = coerce(string(), (value) => {
  if (typeof value === "string") return value;
  if (value == null) return "";
  return String(value);
});

Механика:

  1. входное значение проходит через функцию преобразования
  2. результат передаётся в базовую структуру
  3. применяется стандартная проверка string()

Типичные применения:

  • нормализация API-ответов
  • обработка query-параметров
  • приведение чисел и boolean к строке

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

Для строковых структур возможно задание значения по умолчанию через defaulted.

import { string, defaulted } from "superstruct";

const OptionalName = defaulted(string(), "anonymous");

Поведение:

  • если значение отсутствует → используется "anonymous"
  • если значение передано → выполняется стандартная проверка строки

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


Комбинирование ограничений

Superstruct позволяет свободно комбинировать модификаторы. Это формирует декларативные цепочки правил.

Пример комплексной структуры:

import { string, size, pattern, refine } from "superstruct";

const Username = refine(
  pattern(size(string(), 3, 16), /^[a-zA-Z0-9_]+$/),
  "Username",
  (value) => !value.startsWith("_")
);

Здесь применяются сразу несколько уровней:

  • проверка типа строки
  • ограничение длины
  • проверка допустимых символов
  • бизнес-правило (не начинаться с _)

Работа с пробелами и нормализация

Строковые данные часто требуют предварительной очистки. Superstruct не навязывает автоматическую нормализацию, но позволяет встроить её через coerce.

const TrimmedString = coerce(string(), (value) => {
  if (typeof value !== "string") return value;
  return value.trim();
});

Дальнейшие проверки выполняются уже на нормализованном значении.

Расширенный вариант:

const CleanString = coerce(string(), (value) => {
  if (typeof value !== "string") return value;
  return value.trim().replace(/\s+/g, " ");
});

Такой подход используется для:

  • пользовательского ввода
  • комментариев
  • заголовков и описаний

Вложенные ограничения и композиция

Одно из ключевых свойств Superstruct — возможность композиции структур без потери читаемости логики.

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

const BaseString = string();

const ShortText = size(BaseString, 0, 50);
const LongText = size(BaseString, 0, 500);

Такой подход позволяет:

  • централизовать базовый тип
  • переиспользовать ограничения
  • избегать дублирования логики

Поведение ошибок валидации

При нарушении правил string-структуры возвращается объект ошибки, содержащий:

  • путь до значения
  • ожидаемый тип
  • фактическое значение
  • описание нарушения

Пример сценария:

  • ожидалась строка
  • получено число 42

Результат:

  • тип ошибки: несоответствие структуры
  • сообщение: ожидается строка

При использовании refine или pattern добавляется дополнительный уровень детализации причины.


Optional и nullable строки

Superstruct поддерживает расширение типов через optional и nullable.

import { string, optional, nullable } from "superstruct";

const OptionalString = optional(string());
const NullableString = nullable(string());

Различия:

  • optional — значение может отсутствовать
  • nullable — значение может быть null

Комбинирование:

const FlexibleString = optional(nullable(string()));

Практическое применение строковых структур

Строковые структуры применяются во всех слоях приложений:

API-валидация

  • входящие JSON-запросы
  • query-параметры
  • заголовки

Формы

  • логины
  • пароли (частично как строка)
  • текстовые поля

Хранение данных

  • нормализация перед записью в БД
  • контроль форматов

Интеграции

  • внешние API
  • webhook-пейлоады
  • обмен сообщениями между сервисами

Расширение логики через пользовательские проверки

Когда стандартных средств недостаточно, применяется refine с расширенной логикой.

Пример проверки домена:

const DomainString = refine(string(), "DomainString", (value) => {
  return value.includes(".");
});

Или более строгий вариант:

const SafeString = refine(string(), "SafeString", (value) => {
  return !/[<>]/.test(value);
});

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