В библиотеке Superstruct работа с датами основана на строгой
типизации значений и их проверке через встроенные и расширяемые
структуры. Основная цель — обеспечить предсказуемую валидацию объектов
Date, строковых представлений дат и пользовательских
форматов, сохраняя контроль над преобразованием и ошибками.
Функция date() создаёт структурный валидатор, который
проверяет, является ли значение экземпляром Date и содержит
ли оно корректную временную метку.
import { date } from 'superstruct';
const DateStruct = date();
Такой структурный тип допускает только значения, созданные через
конструктор Date, при этом исключаются строки, числа и
любые невалидные объекты.
Основной принцип работы заключается в проверке через
instanceof Date и дополнительной проверке на валидность
временной метки.
DateStruct.assert(new Date()); // проходит проверку
DateStruct.assert('2024-01-01'); // ошибка
DateStruct.assert(1700000000000); // ошибка
Важный момент — даже валидный объект Date, содержащий
Invalid Date, считается некорректным.
const d = new Date('invalid');
DateStruct.assert(d); // ошибка
Проверка включает контроль метода getTime(). Если
результат — NaN, структура считается нарушенной.
const isValidDate = (value) =>
value instanceof Date && !isNaN(value.getTime());
Superstruct использует аналогичную идею внутри своей реализации для обеспечения строгой проверки.
Часто данные приходят в виде строк ISO-формата. Для этого применяется
механизм преобразования через coerce.
import { coerce, date } from 'superstruct';
const CoercedDate = coerce(date(), string(), (value) => new Date(value));
Теперь строка автоматически преобразуется в Date перед
проверкой.
CoercedDate.assert('2024-05-01T10:00:00Z'); // проходит
Если строка не может быть распознана, результатом станет
Invalid Date, что приведёт к ошибке валидации.
Архитектура Superstruct разделяет два процесса:
Это позволяет явно контролировать поток данных:
const structure = coerce(date(), string(), (v) => new Date(v));
const [error, value] = structure.validate('2025-01-01');
Если требуется строгий режим без преобразований, используется только
date().
Для более строгих условий используется refine,
позволяющий добавлять пользовательские ограничения.
import { refine, date } from 'superstruct';
const FutureDate = refine(date(), 'FutureDate', (value) => {
return value.getTime() > Date.now();
});
Теперь структура допускает только даты в будущем.
FutureDate.assert(new Date('2030-01-01')); // проходит
FutureDate.assert(new Date('2000-01-01')); // ошибка
Частый сценарий — проверка диапазонов времени.
const DateRange = refine(date(), 'DateRange', (value) => {
const min = new Date('2020-01-01');
const max = new Date('2030-01-01');
return value >= min && value <= max;
});
Такая конструкция позволяет ограничивать допустимый временной интервал.
Дата часто используется как часть сложных структур:
import { object, string } from 'superstruct';
const Event = object({
title: string(),
createdAt: date(),
});
Event.assert({
title: 'Meeting',
createdAt: new Date(),
});
При использовании coerce можно автоматически принимать
строки:
const Event = object({
title: string(),
createdAt: CoercedDate,
});
Ошибки валидации содержат информацию о пути, типе и причине нарушения.
try {
DateStruct.assert('not-a-date');
} catch (e) {
console.log(e.failures());
}
Результат включает описание несоответствия типа и ожидаемой структуры.
Superstruct не интерпретирует временные зоны самостоятельно — это
задача JavaScript Date. Однако важно учитывать, что:
new Date('2024-01-01T00:00:00Z');
В реальных приложениях часто требуется поддержка нестандартных форматов.
const CustomDate = coerce(date(), string(), (value) => {
const [day, month, year] = value.split('.');
return new Date(`${year}-${month}-${day}`);
});
Теперь строка 31.12.2024 корректно преобразуется в
объект Date.
В случаях, когда входные данные уже нормализованы, используется чистая проверка:
const StrictDate = date();
Это исключает любые неявные преобразования и повышает предсказуемость.
При работе с API важно помнить, что Date при
сериализации превращается в строку:
JSON.stringify({ date: new Date() });
Superstruct в этом случае должен использовать coerce,
иначе валидация не пройдёт.
Хотя библиотека не предоставляет встроенных операторов сравнения, они
легко реализуются через refine:
const NotPast = refine(date(), 'NotPast', (value) => {
return value.getTime() >= Date.now();
});
Проверка дат в Superstruct имеет минимальные накладные расходы:
instanceof проверкаgetTime()refineОсновная нагрузка появляется только при кастомных преобразованиях.
Дата может участвовать в гибких структурах:
import { union, optional } from 'superstruct';
const MaybeDate = optional(date());
const Flexible = union([date(), string()]);
В последнем случае часто добавляется coerce для
нормализации.
При обработке пользовательского ввода дата чаще всего поступает как строка. Типичный паттерн:
const FormSchema = object({
birthday: coerce(date(), string(), (v) => new Date(v)),
});
Это позволяет унифицировать входные данные независимо от источника.
Встроенный Date в JavaScript имеет особенности:
Superstruct компенсирует это через строгую валидацию и явные преобразования.
Гибкость достигается через комбинацию базовых элементов:
const SafeDate = refine(
coerce(date(), string(), (v) => new Date(v)),
'SafeDate',
(value) => !isNaN(value.getTime())
);
Такая структура объединяет преобразование и проверку в одном типе.
При работе с событиями часто требуется комплексная логика:
const EventDate = refine(date(), 'EventDate', (value) => {
const now = Date.now();
const maxFuture = now + 1000 * 60 * 60 * 24 * 365;
return value.getTime() >= now && value.getTime() <= maxFuture;
});
Подобные конструкции позволяют ограничивать диапазон допустимых дат в бизнес-логике без внешних зависимостей.