Superstruct для TypeScript: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в 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); // Ошибка
size(schema, min, max)

Описание: Проверяет, что размер (длина строки, количество элементов массива) находится в заданном диапазоне.

Параметры: 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 и другие.

Ссылки и ресурсы

Рекомендаций

Bean Validation (Jakarta Validation) — Валидация данных в Java

Bean Validation (Jakarta Validation) — Валидация данных в Java

Работа с ZIP-архивами в Java: полное руководство по java.util.zip

Работа с ZIP-архивами в Java: полное руководство по java.util.zip

Работа с XML в Go (Golang): стандартная библиотека encoding/xml

Работа с XML в Go (Golang): стандартная библиотека encoding/xml

Twig для PHP: полное руководство с примерами

Twig для PHP: полное руководство с примерами