Babel Plugin: TypeScript Decorator Metadata

0.4.0 · active · verified Sun Apr 19

This Babel plugin enables the emission of design-time type metadata for TypeScript decorators when transpiling with Babel. It replicates the functionality provided by the TypeScript compiler's `emitDecoratorMetadata` flag, which is essential for frameworks and libraries like NestJS and TypeORM that rely on runtime type reflection for features such as Dependency Injection. The current stable version is `0.4.0`, released in October 2025, which introduced support for Babel 8. The project has a relatively slow release cadence, indicating a maintenance-driven development. It differentiates itself by bridging a crucial gap in Babel's TypeScript preset, allowing developers to leverage advanced decorator patterns without needing the TypeScript compiler for compilation.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates a TypeScript application using decorators for dependency injection, showcasing how `babel-plugin-transform-typescript-metadata` enables runtime type reflection. It includes an example with InversifyJS, illustrating both constructor and property injection, and explicitly retrieves emitted metadata via `Reflect.getMetadata`.

import 'reflect-metadata'; // Essential for runtime metadata access
import { injectable, inject } from 'inversify'; // Example: InversifyJS for DI

// Define an interface for clarity
interface Logger { 
  log(message: string): void;
}

// Implement the interface and make it injectable
@injectable()
class ConsoleLogger implements Logger {
  private readonly prefix: string;

  constructor(@inject('logPrefix') prefix: string) {
    this.prefix = prefix;
  }

  log(message: string): void {
    console.log(`[${this.prefix}] ${message}`);
  }
}

// A service that depends on a Logger
@injectable()
class DataService {
  @inject('Logger') // Property injection example
  private readonly logger!: Logger;

  private readonly apiUrl: string;

  constructor(@inject('apiUrl') apiUrl: string) { // Constructor injection example
    this.apiUrl = apiUrl;
  }

  fetchData(): void {
    this.logger.log(`Fetching data from ${this.apiUrl}...`);
    // Simulate fetching data
    const data = { id: 1, name: 'Sample Data' };
    this.logger.log(`Data received: ${JSON.stringify(data)}`);

    // Demonstrate emitted metadata (runtime reflection)
    console.log('\n--- Runtime Metadata for DataService.logger ---');
    const loggerType = Reflect.getMetadata('design:type', DataService.prototype, 'logger');
    console.log('design:type (logger):', loggerType ? loggerType.name : 'unknown');

    console.log('\n--- Runtime Metadata for ConsoleLogger constructor ---');
    const constructorParams = Reflect.getMetadata('design:paramtypes', ConsoleLogger);
    console.log('design:paramtypes (ConsoleLogger):', constructorParams ? constructorParams.map((p: any) => p.name) : 'unknown');
  }
}

// To run this, you would typically use a Babel setup:
// 1. Install dependencies: `npm install --save-dev @babel/core @babel/preset-typescript @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties`
//    `npm install reflect-metadata inversify`
// 2. Configure Babel (e.g., in babel.config.js):
//    module.exports = {
//      plugins: [
//        'babel-plugin-transform-typescript-metadata',
//        ['@babel/plugin-proposal-decorators', { 'legacy': true }],
//        ['@babel/plugin-proposal-class-properties', { 'loose': true }],
//      ],
//      presets: [
//        '@babel/preset-typescript',
//      ],
//    };

// --- Manual setup to run the example without a full Inversify container ---
// (In a real app, an Inversify container would handle instantiation and injection)

// Mock 'inject' for compile-time type checking and demonstration
class FakeInject { constructor(id: string) { return (target: any, key: string, index?: number) => {}; } }
const fakeInject = (id: string) => new FakeInject(id) as any;

// Manually create instances and inject for demonstration
const logPrefix = 'APP';
const consoleLogger: Logger = new ConsoleLogger(logPrefix);

const apiUrl = 'https://api.example.com/data';
const dataService = new DataService(apiUrl);

// Simulate property injection
(dataService as any).logger = consoleLogger;

dataService.fetchData();

view raw JSON →