Введение в Superstruct
Superstruct — это легковесная, но мощная библиотека для валидации и парсинга структур данных в TypeScript и JavaScript. Она позволяет вам описывать ожидаемую форму данных (объекты, массивы, строки, числа и т.д.) с помощью простых и выразительных "схем" (structs), а затем проверять, соответствуют ли реальные данные этим схемам.
В отличие от других библиотек валидации, таких как Zod или Yup, Superstruct фокусируется на простоте, производительности и минимализме. Она не генерирует сложные типы TypeScript автоматически (хотя и дружит с ними), а вместо этого предлагает гибкий и интуитивно понятный API для описания структур.
Зачем нужна Superstruct?
- Валидация данных из внешних источников: API-ответы, данные из форм, конфигурационные файлы, localStorage — везде, где данные могут прийти в неожиданном формате.
- Создание безопасных DTO (Data Transfer Objects): Гарантирует, что данные, передаваемые между слоями приложения, соответствуют ожидаемой структуре.
- Парсинг и трансформация: Superstruct может не только проверять, но и преобразовывать данные (например, приводить строки к числам).
- Читаемая документация: Схемы Superstruct сами по себе являются отличной документацией того, как выглядят данные.
Установка
Superstruct — это npm-пакет, поэтому установка производится через менеджер пакетов Node.js:
npm install superstruct
Или, если вы используете Yarn:
yarn add superstruct
После установки вы можете импортировать библиотеку в свой TypeScript-файл:
import { struct, string, number, object, array, optional, union, literal, enums, size, refine, assert, is, validate } from 'superstruct';
Быстрый старт
Давайте создадим простую схему для пользователя и проверим данные.
import { object, string, number, assert } from 'superstruct';
// 1. Определяем схему (struct)
const User = object({
id: number(),
name: string(),
email: string(),
});
// 2. Данные, которые мы хотим проверить
const userData = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
};
// 3. Валидация
try {
assert(userData, User);
console.log('Данные валидны!');
} catch (e) {
console.error('Ошибка валидации:', e.message);
}
// 4. Если данные неверны, assert выбросит исключение
const badData = { id: 'не число', name: 'Bob' };
try {
assert(badData, User);
} catch (e) {
console.error('Ошибка:', e.message);
// Вывод: Expected a number for `id`, but received: "не число"
}
Основные методы и функции
Superstruct предоставляет набор фабрик для создания схем. Вот 10 основных:
1. string()
Описание: Создает схему, которая проверяет, что значение является строкой.
Параметры: Нет.
import { string, assert } from 'superstruct';
const NameSchema = string();
assert('Hello', NameSchema); // OK
// assert(123, NameSchema); // Ошибка: Expected a string, but received: 123
2. number()
Описание: Проверяет, что значение является числом (включая NaN, если не указано иное).
Параметры: Нет.
import { number, assert } from 'superstruct';
const AgeSchema = number();
assert(30, AgeSchema); // OK
// assert('30', AgeSchema); // Ошибка
3. boolean()
Описание: Проверяет, что значение является булевым (true или false).
Параметры: Нет.
import { boolean, assert } from 'superstruct';
const IsActiveSchema = boolean();
assert(true, IsActiveSchema); // OK
4. object(schema)
Описание: Создает схему для объекта с определенными ключами и типами значений. По умолчанию все ключи обязательны.
Параметры: schema: Record<string, Struct> — объект, где ключи — это имена полей, а значения — схемы для этих полей.
import { object, string, number, assert } from 'superstruct';
const Product = object({
title: string(),
price: number(),
});
assert({ title: 'Laptop', price: 999.99 }, Product); // OK
// assert({ title: 'Laptop' }, Product); // Ошибка: поле price обязательно
5. array(elementSchema)
Описание: Создает схему для массива, где каждый элемент соответствует переданной схеме.
Параметры: elementSchema: Struct — схема для элементов массива.
import { array, string, assert } from 'superstruct';
const TagsSchema = array(string());
assert(['typescript', 'javascript'], TagsSchema); // OK
// assert(['typescript', 123], TagsSchema); // Ошибка
6. optional(schema)
Описание: Делает поле необязательным. Если значение undefined, валидация проходит успешно.
Параметры: schema: Struct — схема для значения, если оно присутствует.
import { object, string, optional, assert } from 'superstruct';
const User = object({
name: string(),
email: optional(string()),
});
assert({ name: 'Alice' }, User); // OK (email отсутствует)
assert({ name: 'Bob', email: 'bob@test.com' }, User); // OK
// assert({ name: 'Bob', email: 123 }, User); // Ошибка: email должен быть строкой
7. union(...schemas)
Описание: Создает схему, которая проверяет, что значение соответствует хотя бы одной из переданных схем (логическое ИЛИ).
Параметры: ...schemas: Struct[] — список возможных схем.
import { union, string, number, assert } from 'superstruct';
const IdSchema = union([string(), number()]);
assert('abc', IdSchema); // OK
assert(123, IdSchema); // OK
// assert(true, IdSchema); // Ошибка
8. literal(value)
Описание: Проверяет, что значение строго равно указанному литералу (используется ===).
Параметры: value: any — точное значение, которому должны соответствовать данные.
import { literal, assert } from 'superstruct';
const StatusSchema = literal('active');
assert('active', StatusSchema); // OK
// assert('inactive', StatusSchema); // Ошибка
9. enums(values)
Описание: Проверяет, что значение является одним из перечисленных в массиве.
Параметры: values: any[] — массив допустимых значений.
import { enums, assert } from 'superstruct';
const RoleSchema = enums(['admin', 'user', 'guest']);
assert('admin', RoleSchema); // OK
assert('user', RoleSchema); // OK
// assert('moderator', RoleSchema); // Ошибка
Описание: Проверяет, что размер (длина строки, количество элементов массива) находится в заданном диапазоне.
Параметры: schema: Struct — схема для значения (обычно string() или array()), min: number, max: number.
import { size, string, assert } from 'superstruct';
const PasswordSchema = size(string(), 8, 20);
assert('password123', PasswordSchema); // OK (длина 11)
// assert('short', PasswordSchema); // Ошибка: длина 5 меньше 8
Реальный практический пример
Представьте, что вы пишете функцию для обработки данных, полученных от внешнего API погоды. Данные могут быть неполными или иметь неверный тип. Используем Superstruct для безопасного парсинга.
import { object, string, number, optional, array, assert, StructError } from 'superstruct';
// Схема для данных о погоде
const WeatherData = object({
city: string(),
temperature: number(),
humidity: optional(number()),
conditions: array(string()),
});
// Функция для безопасного получения данных
function fetchWeatherData(rawData: unknown): { city: string; temperature: number; conditions: string[] } {
try {
// Пытаемся утвердить, что данные соответствуют схеме
assert(rawData, WeatherData);
// Если успешно, rawData теперь имеет правильный тип
return rawData;
} catch (error) {
if (error instanceof StructError) {
console.error('Ошибка валидации погоды:', error.message);
// Можно выбросить более понятную ошибку или вернуть значения по умолчанию
throw new Error(`Неверный формат данных погоды: ${error.message}`);
}
throw error; // Перебрасываем неизвестные ошибки
}
}
// Пример использования
const apiResponse = {
city: 'Moscow',
temperature: 22,
conditions: ['clear', 'windy'],
};
try {
const weather = fetchWeatherData(apiResponse);
console.log(`Погода в ${weather.city}: ${weather.temperature}°C`);
} catch (e) {
console.error(e.message);
}
// Пример с некорректными данными
const badResponse = { city: 'London', temperature: 'холодно' };
try {
fetchWeatherData(badResponse);
} catch (e) {
// Вывод: Неверный формат данных погоды: Expected a number for `temperature`, but received: "холодно"
}
Советы и типичные ошибки
- Ошибка: Забыли обернуть схему в фабрику.
string— это фабрика, её нужно вызывать:string().const schema = string— это функция, а не схема. - Совет: Используйте
is()для простой проверки. Если вам не нужно выбрасывать исключение, используйтеis(data, schema), который возвращаетtrueилиfalse. - Ошибка: Путаница между
optionalиnullable.optionalдопускаетundefined, но неnull. Дляnullиспользуйтеnullable(schema)(если импортировали). - Совет: Используйте
refine()для кастомной валидации. Если встроенных схем недостаточно,refine(schema, validator, message)позволяет добавить свою функцию проверки. - Ошибка: Не обрабатываете ошибки
StructError. Всегда оборачивайте вызовassert()в try/catch, чтобы корректно обрабатывать неверные данные. - Совет: Комбинируйте схемы. Superstruct позволяет создавать сложные вложенные структуры, комбинируя
object,array,unionи другие.
Ссылки и ресурсы
- Официальный репозиторий на GitHub: ianstormtaylor/superstruct
- Документация на сайте библиотеки:
Также в библиотеке
SwiftyUserDefaults для Swift: полное руководство с примерами
FireO – ORM для Google Firestore
Работа с S3 и облачными хранилищами в Java: AWS SDK и MinIO
pybind11 для C++: полное руководство с примерами
Очереди задач в Swift: GCD, OperationQueue и Swift Concurrency
Рекомендаций