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); Также в библиотеке
Работа с S3 и облачными хранилищами в JavaScript (Node.js) — библиотека @aws-sdk/client-s3
Psr/Log для PHP: полное руководство с примерами
neverthrow для TypeScript: полное руководство с примерами
Работа с датами в Swift (iOS/macOS): библиотеки и инструменты
Логирование в JavaScript (Node.js): лучшие решения для вашего проекта