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

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

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

Начать курс

ArkType для TypeScript: полное руководство

1. Что это и зачем нужна

ArkType — это современная библиотека для валидации данных в TypeScript, которая работает на уровне типов. В отличие от традиционных валидаторов (Zod, Yup), ArkType использует уникальный подход: он преобразует определения типов в оптимизированные парсеры и валидаторы на этапе компиляции. Это позволяет достичь высокой производительности и полной интеграции с системой типов TypeScript.

Зачем нужна ArkType? В современных веб-приложениях данные часто приходят из внешних источников: API, формы, localStorage, WebSocket. Без валидации вы рискуете получить ошибки времени выполнения, которые сложно отловить. ArkType решает эту проблему, предоставляя декларативный способ описания схем данных и автоматическую генерацию TypeScript-типов из этих схем. Библиотека особенно полезна при работе с REST API, GraphQL, формами и любыми внешними данными.

Ключевое преимущество ArkType — скорость. Благодаря статической компиляции схем, валидация выполняется в 10-100 раз быстрее, чем у аналогов. Кроме того, ArkType поддерживает рекурсивные типы, циклические ссылки, строгие проверки и множество встроенных типов. Это делает её идеальным выбором для высоконагруженных приложений и микросервисов.

2. Установка

Установка ArkType выполняется через пакетный менеджер npm (или yarn/pnpm). Библиотека написана на TypeScript и поставляется с полной поддержкой типов.

npm install arktype

Для работы с TypeScript дополнительных настроек не требуется — библиотека автоматически подхватывает конфигурацию tsconfig.json. Минимальная версия TypeScript — 5.0.

# Если используете yarn
yarn add arktype

# Если используете pnpm
pnpm add arktype

3. Быстрый старт

Минимальный пример использования ArkType — создание схемы для валидации объекта пользователя:

import { type } from 'arktype';

// Определяем схему
const User = type({
    name: 'string',
    age: 'number',
    email: 'string.email'
});

// Валидируем данные
const result = User({
    name: 'Иван',
    age: 25,
    email: 'ivan@example.com'
});

if (result instanceof type.errors) {
    console.error('Ошибка валидации:', result.summary);
} else {
    console.log('Данные валидны:', result);
    // result автоматически имеет тип { name: string; age: number; email: string }
}

В этом примере мы создали схему User, которая проверяет три поля. Если данные не проходят валидацию, возвращается объект ошибки с подробным описанием. Если всё ок — возвращаются типизированные данные.

4. Основные методы и функции

ArkType предоставляет богатый API для создания схем, их комбинирования и трансформации. Рассмотрим 8 ключевых методов.

4.1. type() — создание схемы

Сигнатура: type(schema: TypeSchema): Type

Описание: Основной метод для создания схемы валидации. Принимает объект или строку с определением типа.

import { type } from 'arktype';

// Простая схема
const StringType = type('string');
const NumberType = type('number');

// Объектная схема
const Person = type({
    name: 'string',
    age: 'number',
    isActive: 'boolean'
});

// Валидация
console.log(StringType('hello')); // 'hello'
console.log(StringType(123)); // Ошибка: Expected string, got number

4.2. .array() — массив элементов

Сигнатура: type.array(): Type

Описание: Создаёт схему для массива элементов указанного типа.

import { type } from 'arktype';

const Numbers = type('number').array();
const Users = type({
    name: 'string',
    age: 'number'
}).array();

// Валидация
console.log(Numbers([1, 2, 3])); // [1, 2, 3]
console.log(Numbers([1, '2', 3])); // Ошибка: Expected number at index 1
console.log(Users([{ name: 'Иван', age: 25 }])); // Массив пользователей

4.3. .optional() — опциональное поле

Сигнатура: type.optional(): Type

Описание: Делает поле необязательным. Если значение отсутствует или undefined, валидация проходит успешно.

import { type } from 'arktype';

const User = type({
    name: 'string',
    age: 'number.optional',
    email: 'string.email.optional'
});

// Валидация
console.log(User({ name: 'Иван' })); // { name: 'Иван' }
console.log(User({ name: 'Иван', age: 25 })); // { name: 'Иван', age: 25 }
console.log(User({ name: 'Иван', email: 'не email' })); // Ошибка: Invalid email

4.4. .union() — объединение типов

Сигнатура: type.union(...types: Type[]): Type

Описание: Создаёт схему, которая принимает значение, соответствующее хотя бы одному из переданных типов.

import { type } from 'arktype';

const StringOrNumber = type('string').union(type('number'));
const Status = type('"active"').union(type('"inactive"')).union(type('"pending"'));

// Валидация
console.log(StringOrNumber('hello')); // 'hello'
console.log(StringOrNumber(42)); // 42
console.log(StringOrNumber(true)); // Ошибка: Expected string | number
console.log(Status('active')); // 'active'
console.log(Status('deleted')); // Ошибка: Expected "active" | "inactive" | "pending"

4.5. .intersection() — пересечение типов

Сигнатура: type.intersection(...types: Type[]): Type

Описание: Создаёт схему, которая проверяет, что значение соответствует всем переданным типам одновременно.

import { type } from 'arktype';

const HasName = type({ name: 'string' });
const HasAge = type({ age: 'number' });
const Person = HasName.intersection(HasAge);

// Валидация
console.log(Person({ name: 'Иван', age: 25 })); // { name: 'Иван', age: 25 }
console.log(Person({ name: 'Иван' })); // Ошибка: Missing required field 'age'
console.log(Person({ name: 'Иван', age: '25' })); // Ошибка: Expected number for 'age'

4.6. .pick() — выбор полей

Сигнатура: type.pick(...keys: string[]): Type

Описание: Создаёт новую схему, включающую только указанные поля из исходной объектной схемы.

import { type } from 'arktype';

const FullUser = type({
    id: 'number',
    name: 'string',
    email: 'string.email',
    password: 'string'
});

const PublicUser = FullUser.pick('id', 'name', 'email');

// Валидация
console.log(PublicUser({
    id: 1,
    name: 'Иван',
    email: 'ivan@example.com'
})); // { id: 1, name: 'Иван', email: 'ivan@example.com' }

console.log(PublicUser({
    id: 1,
    name: 'Иван',
    email: 'ivan@example.com',
    password: 'secret'
})); // Ошибка: Unexpected field 'password'

4.7. .omit() — исключение полей

Сигнатура: type.omit(...keys: string[]): Type

Описание: Создаёт новую схему, исключающую указанные поля из исходной объектной схемы.

import { type } from 'arktype';

const FullUser = type({
    id: 'number',
    name: 'string',
    email: 'string.email',
    password: 'string'
});

const SafeUser = FullUser.omit('password');

// Валидация
console.log(SafeUser({
    id: 1,
    name: 'Иван',
    email: 'ivan@example.com'
})); // { id: 1, name: 'Иван', email: 'ivan@example.com' }

console.log(SafeUser({
    id: 1,
    name: 'Иван',
    email: 'ivan@example.com',
    password: 'secret'
})); // Ошибка: Unexpected field 'password'

4.8. .pipe() — трансформация данных

Сигнатура: type.pipe(transform: (value: T) => U): Type

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

import { type } from 'arktype';

const StringToNumber = type('string').pipe((s: string) => parseInt(s, 10));
const TrimmedString = type('string').pipe((s: string) => s.trim());

// Валидация с трансформацией
console.log(StringToNumber('42')); // 42 (число)
console.log(StringToNumber('не число')); // NaN (но валидация пройдена)
console.log(TrimmedString('  hello  ')); // 'hello' (обрезано)

// Комбинирование с валидацией
const PositiveInt = type('string')
    .pipe((s: string) => parseInt(s, 10))
    .pipe((n: number) => {
        if (n <= 0) throw new Error('Число должно быть положительным');
        return n;
    });

console.log(PositiveInt('5')); // 5
console.log(PositiveInt('-1')); // Ошибка: Число должно быть положительным

5. Реальный практический пример

Рассмотрим комплексный пример — создание системы валидации для REST API с обработкой ошибок и трансформацией данных.

import { type } from 'arktype';

// 1. Определяем схемы для различных сущностей
const Address = type({
    street: 'string',
    city: 'string',
    zipCode: 'string.pipe((s: string) => s.replace(/\s/g, ""))', // Удаляем пробелы
    country: 'string'
});

const UserCreate = type({
    name: 'string.min(2).max(50)',
    email: 'string.email',
    age: 'number.integer.min(0).max(150)',
    address: Address.optional(),
    roles: type('"admin"').union(type('"user"')).union(type('"moderator"')).array()
});

const UserUpdate = UserCreate.partial(); // Все поля опциональны

// 2. Создаём класс для работы с пользователями
class UserService {
    async createUser(data: unknown) {
        const result = UserCreate(data);
        
        if (result instanceof type.errors) {
            return {
                success: false,
                errors: result.summary.map(err => ({
                    path: err.path,
                    message: err.message,
                    code: err.code
                }))
            };
        }
        
        // Трансформируем данные перед сохранением
        const user = {
            ...result,
            createdAt: new Date(),
            updatedAt: new Date(),
            isActive: true
        };
        
        // Сохраняем в базу данных (имитация)
        console.log('Сохранён пользователь:', user);
        return { success: true, data: user };
    }

    async updateUser(id: number, data: unknown) {
        const result = UserUpdate(data);
        
        if (result instanceof type.errors) {
            return {
                success: false,
                errors: result.summary.map(err => ({
                    path: err.path,
                    message: err.message,
                    code: err.code
                }))
            };
        }
        
        // Обновляем пользователя (имитация)
        console.log(`Обновлён пользователь ${id}:`, result);
        return { success: true, data: result };
    }

    async validateBulk(users: unknown[]) {
        const UsersArray = UserCreate.array();
        const result = UsersArray(users);
        
        if (result instanceof type.errors) {
            return {
                success: false,
                errors: result.summary.map(err => ({
                    index: err.path?.[0],
                    message: err.message
                }))
            };
        }
        
        return { success: true, data: result };
    }
}

// 3. Пример использования
async function main() {
    const service = new UserService();

    // Успешное создание
    const createResult = await service.createUser({
        name: 'Иван Петров',
        email: 'ivan@example.com',
        age: 30,
        address: {
            street: 'ул. Ленина, 10',
            city: 'Москва',
            zipCode: '101000',
            country: 'Россия'
        },
        roles: ['admin', 'user']
    });
    console.log('Результат создания:', createResult);

    // Ошибка валидации
    const errorResult = await service.createUser({
        name: 'А',
        email: 'не email',
        age: -5,
        roles: ['superadmin']
    });
    console.log('Ошибки:', errorResult);

    // Валидация массива пользователей
    const bulkResult = await service.validateBulk([
        { name: 'Пользователь 1', email: 'user1@test.com', age: 25, roles: ['user'] },
        { name: 'Пользователь 2', email: 'user2@test.com', age: 30, roles: ['user'] }
    ]);
    console.log('Массовая валидация:', bulkResult);
}

main().catch(console.error);

Этот пример демонстрирует реальное использование ArkType в серверном приложении: создание сложных схем с вложенными объектами, массивами, трансформациями, обработку ошибок и массовую валидацию.

6. Советы по производительности и типичные ошибки

Советы по

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