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

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

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

Начать курс

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

В мире TypeScript существует фундаментальная проблема: типы существуют только на этапе компиляции. После транспиляции в JavaScript вся информация о типах исчезает. Это делает невозможным runtime-валидацию, динамическую сериализацию или построение сложных систем, зависящих от метаданных типов. Библиотека deepkit/type решает эту проблему кардинально — она переносит систему типов TypeScript в runtime, позволяя работать с типами как с данными.

Основная ценность deepkit/type заключается в том, что она не требует написания дублирующих схем (как в Zod или Yup). Вы описываете типы один раз — на чистом TypeScript, а библиотека автоматически извлекает их в runtime. Это даёт возможность выполнять валидацию данных, сериализацию/десериализацию, рефлексию (получение информации о свойствах, дженериках, декораторах) и даже генерировать код на основе типов. deepkit/type — это не просто валидатор, а полноценный фреймворк для метапрограммирования.

Библиотека идеально подходит для проектов, где важна строгая типизация на всех уровнях: API-серверы (валидация входящих запросов), базы данных (маппинг типов в SQL), конфигурационные файлы, RPC-системы и инструменты для code generation. Благодаря интеграции с deepkit/framework, она также используется для создания высокопроизводительных серверных приложений с автоматической валидацией и сериализацией.

Установка

deepkit/type — это npm-пакет для TypeScript. Установка производится стандартной командой:

npm install @deepkit/type reflect-metadata

Также вам потребуется настроить tsconfig.json для поддержки декораторов и emit-метаданных:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strict": true,
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist"
  }
}

Если вы используете ts-node для запуска, убедитесь, что он загружает reflect-metadata первым. Добавьте в начало вашего entry-файла:

import 'reflect-metadata';

Быстрый старт

Минимальный пример демонстрирует, как объявить тип и получить его runtime-представление:

import 'reflect-metadata';
import { typeOf, ReflectionClass } from '@deepkit/type';

// Объявляем интерфейс
interface User {
  id: number;
  name: string;
  email?: string;
}

// Получаем runtime-тип
const userType = typeOf();
console.log(userType); // ReflectionClass

// Проверяем свойства
const reflection = ReflectionClass.from(userType);
console.log(reflection.getProperty('name').type); // ReflectionProperty
console.log(reflection.getProperty('id').type.kind); // 'number'

Этот код выведет информацию о типе User в консоль. Обратите внимание: мы не писали никаких схем — только чистый TypeScript-интерфейс.

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

Ниже приведены ключевые возможности deepkit/type. Для каждого метода указана сигнатура, краткое описание и рабочий пример.

1. typeOf

Сигнатура: typeOf<T>(): ReflectionClass

Что делает: Извлекает runtime-представление типа T. Возвращает объект ReflectionClass, содержащий полную информацию о структуре типа: свойства, их типы, модификаторы (optional, readonly), дженерики и декораторы.

import { typeOf } from '@deepkit/type';

interface Product {
  id: number;
  title: string;
  price: number;
}

const productType = typeOf();
console.log(productType.getProperty('title').type.kind); // 'string'
console.log(productType.getProperty('price').type.kind); // 'number'

2. is

Сигнатура: is<T>(value: unknown): value is T

Что делает: Проверяет, соответствует ли переданное значение типу T. Работает как type guard — после проверки TypeScript сужает тип. Поддерживает глубокую вложенность, union-типы, optional-поля.

import { is } from '@deepkit/type';

interface Config {
  port: number;
  host: string;
  debug?: boolean;
}

const data: unknown = { port: 3000, host: 'localhost' };

if (is(data)) {
  // TypeScript знает, что data — это Config
  console.log(data.port); // OK
  console.log(data.host); // OK
}

3. cast

Сигнатура: cast<T>(value: unknown): T

Что делает: Преобразует (кастует) значение к типу T. В отличие от простого приведения типов, cast выполняет глубокую конвертацию: например, строку в число, объект с лишними полями — отбрасывает их, а недостающие заполняет значениями по умолчанию (если они определены).

import { cast } from '@deepkit/type';

interface Point {
  x: number;
  y: number;
}

const raw = { x: '10', y: '20', extra: 'ignored' };
const point = cast(raw);
console.log(point); // { x: 10, y: 20 } — строки стали числами, extra удалено

4. serialize

Сигнатура: serialize<T>(value: T): unknown

Что делает: Сериализует значение в plain JavaScript объект. Полезно для отправки данных по сети или сохранения в JSON. Убирает все лишние поля, преобразует классы в простые объекты, применяет трансформеры (если заданы).

import { serialize } from '@deepkit/type';

class Person {
  constructor(
    public name: string,
    public age: number,
    private ssn: string
  ) {}
}

const person = new Person('Alice', 30, '123-45-6789');
const serialized = serialize(person);
console.log(serialized); // { name: 'Alice', age: 30 } — ssn исключено

5. deserialize

Сигнатура: deserialize<T>(value: unknown): T

Что делает: Обратная операция к serialize. Восстанавливает значение из plain объекта в указанный тип. Может создавать экземпляры классов, если они имеют конструктор.

import { deserialize } from '@deepkit/type';

interface Task {
  id: number;
  title: string;
  completed: boolean;
}

const jsonData = { id: '1', title: 'Learn Deepkit', completed: false };
const task = deserialize(jsonData);
console.log(task); // { id: 1, title: 'Learn Deepkit', completed: false }

6. ReflectionClass.from

Сигнатура: ReflectionClass.from(type: ReflectionClass | Function | object): ReflectionClass

Что делает: Создаёт объект ReflectionClass из различных источников: уже существующего ReflectionClass, конструктора класса или экземпляра объекта. Позволяет динамически анализировать типы.

import { ReflectionClass } from '@deepkit/type';

class Car {
  model: string = '';
  year: number = 2020;
}

// Из класса
const classRef = ReflectionClass.from(Car);
console.log(classRef.getProperty('model').type.kind); // 'string'

// Из экземпляра
const myCar = new Car();
const instanceRef = ReflectionClass.from(myCar);
console.log(instanceRef.getProperty('year').type.kind); // 'number'

7. ReflectionClass.getProperty

Сигнатура: ReflectionClass.getProperty(name: string): ReflectionProperty

Что делает: Возвращает объект ReflectionProperty для указанного свойства. Из него можно получить тип, модификаторы (optional, readonly), декораторы и значение по умолчанию.

import { typeOf, ReflectionClass } from '@deepkit/type';

interface Book {
  isbn: string;
  title: string;
  author?: string;
}

const bookType = typeOf();
const reflection = ReflectionClass.from(bookType);

const titleProp = reflection.getProperty('title');
console.log(titleProp.name); // 'title'
console.log(titleProp.type.kind); // 'string'
console.log(titleProp.isOptional()); // false

const authorProp = reflection.getProperty('author');
console.log(authorProp.isOptional()); // true

8. ReflectionClass.getProperties

Сигнатура: ReflectionClass.getProperties(): ReflectionProperty[]

Что делает: Возвращает массив всех свойств типа. Удобно для итерации по полям, например, для автоматической генерации форм или SQL-запросов.

import { typeOf, ReflectionClass } from '@deepkit/type';

interface Order {
  id: number;
  customerId: number;
  total: number;
  status: 'pending' | 'shipped' | 'delivered';
}

const orderType = typeOf();
const reflection = ReflectionClass.from(orderType);

const properties = reflection.getProperties();
properties.forEach(prop => {
  console.log(`${prop.name}: ${prop.type.kind} (optional: ${prop.isOptional()})`);
});
// Вывод:
// id: number (optional: false)
// customerId: number (optional: false)
// total: number (optional: false)
// status: string (optional: false)

9. ReflectionProperty.getType

Сигнатура: ReflectionProperty.getType(): ReflectionType

Что делает: Возвращает тип свойства в виде ReflectionType. Позволяет рекурсивно анализировать вложенные типы, union-типы, массивы и т.д.

import { typeOf, ReflectionClass } from '@deepkit/type';

interface TreeNode {
  value: number;
  children: TreeNode[];
}

const treeType = typeOf();
const reflection = ReflectionClass.from(treeType);

const childrenProp = reflection.getProperty('children');
const childrenType = childrenProp.getType();
console.log(childrenType.kind); // 'array'
console.log(childrenType.type.kind); // 'object' (тип элемента массива)

10. ReflectionClass.getDecorators

Сигнатура: ReflectionClass.getDecorators(): ReflectionDecorator[]

Что делает: Возвращает декораторы, применённые к классу или интерфейсу. Это позволяет реализовать собственные декораторы для валидации, логирования или других cross-cutting concerns.

import { ReflectionClass, decorator } from '@deepkit/type';

// Создаём кастомный декоратор
function Loggable() {
  return decorator('loggable', {});
}

@Loggable()
class Service {
  doWork() {}
}

const reflection = ReflectionClass.from(Service);
const decorators = reflection.getDecorators();
console.log(decorators); // [ { name: 'loggable', args: {} } ]

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

Создадим простой REST API-сервер с валидацией входящих запросов и сериализацией ответов. Для демонстрации используем встроенный HTTP-сервер Node.js.

import 'reflect-metadata';
import { createServer, IncomingMessage, ServerResponse } from 'http';
import { is, cast, serialize, typeOf, ReflectionClass } from '@deepkit/type';

// --- Определяем типы данных ---

interface CreateUserRequest {
  name: string;
  email: string;
  age: number;
}

interface UserResponse {
  id: number;
  name: string;
  email: string;
  age: number;
  createdAt: string;
}

interface ApiError {
  error: string;
  details?: string;
}

// --- Вспомогательные функции ---

function readBody(req: IncomingMessage): Promise {
  return new Promise((resolve, reject) => {
    let body = '';
    req.on('data', (chunk: Buffer) => {
      body += chunk.toString();
    });
    req.on('end', () => resolve(body));
    req.on('error', reject);
  });
}

function sendJson(res: ServerResponse, status: number, data: unknown) {
  res.writeHead(status, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(serialize(data)));
}

// --- База данных в памяти ---

const users: Map = new Map();
let nextId = 1;

// --- Обработчик запросов ---

async function handleRequest(req: IncomingMessage, res: ServerResponse) {
  const { method, url } = req;

  if (method === 'POST' && url === '/users') {
    // Читаем тело запроса
    const rawBody = await readBody(req);
    let parsedBody: unknown;

    try {
      parsedBody = JSON.parse(rawBody);
    } catch {
      sendJson(res, 400, { error: 'Invalid JSON' });
      return;
    }

    // Валидация с помощью is
    if (!is(parsedBody)) {
      // Получаем информацию о типе для детализации ошибки
      const reqType = typeOf();
      const reflection = ReflectionClass.from(reqType);
      const requiredFields = reflection.getProperties()
        .filter(p => !p.isOptional())
        .map(p => p.name);

      sendJson(res, 400, {
        error: 'Validation failed',
        details: `Required fields: ${requiredFields.join(', ')}`
      });
      return;
    }

    // Кастим к правильному типу (уже проверено is, но cast гарантирует конвертацию)
    const validRequest = cast(parsedBody);

    // Создаём пользователя
    const newUser: UserResponse = {
      id: nextId++,
      name: validRequest.name,
      email: validRequest.email,
      age: validRequest.age,
      createdAt: new Date().toISOString()
    };

    users.set(newUser.id, newUser);                    

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