Как используешь метаданные в TypeScript/Node.js?

«Как используешь метаданные в TypeScript/Node.js?» — вопрос из категории TypeScript, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В контексте TypeScript и Node.js под метаданными я понимаю информацию о типах, структурах или конфигурации, которая доступна во время выполнения (runtime) или используется инструментами на этапе компиляции.

1. Метаданные TypeScript (декораторы и reflect-metadata): Это мощный механизм для добавления информации к классам, методам или свойствам, которую потом можно прочитать.

import 'reflect-metadata';

// Декоратор, который добавляет метаданные
function Log(target: any, key: string) {
  const types = Reflect.getMetadata('design:type', target, key);
  const paramtypes = Reflect.getMetadata('design:paramtypes', target, key);
  console.log(`${key} type: ${types.name}, param types: ${paramtypes?.map(p => p.name)}`);
}

class MyService {
  @Log
  greet(name: string): string {
    return `Hello, ${name}`;
  }
}
// При запуске (с включенной опцией `emitDecoratorMetadata` в tsconfig.json)
// в консоль выведется: greet type: Function, param types: String

Где это применяется:

  • Фреймворки: NestJS, TypeORM, class-validator активно используют декораторы и метаданные для dependency injection, описания моделей БД и валидации.
  • Собственные валидаторы или сериализаторы: Можно создать декоратор @MaxLength(10) и в runtime проверить значение поля.

2. Метаданные пакетов (package.json): В Node.js package.json — это основные метаданные модуля. Я использую их для:

{
  "name": "my-service",
  "version": "1.0.0",
  "engines": { "node": ">=18.0.0" }, // Метаданные о среде
  "config": { "defaultPort": 3000 } // Кастомная конфигурация
}

Доступ через process.env.npm_package_config_defaultPort.

3. Метаданные в HTTP-запросах/ответах: Заголовки (X-Request-Id, User-Agent) — это форма метаданных для отслеживания и логирования.

app.use((req, res, next) => {
  req.id = uuid.v4(); // Добавляем метаданные к объекту запроса
  res.setHeader('X-Response-Time', Date.now() - req.startTime);
  next();
});