{"id":10548,"library":"babel-plugin-transform-typescript-metadata","title":"Babel Plugin: TypeScript Decorator Metadata","description":"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.","status":"active","version":"0.4.0","language":"javascript","source_language":"en","source_url":"https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata","tags":["javascript","babel","babel-plugin","babel-typescript","decorators","reflect-metadata","typescript"],"install":[{"cmd":"npm install babel-plugin-transform-typescript-metadata","lang":"bash","label":"npm"},{"cmd":"yarn add babel-plugin-transform-typescript-metadata","lang":"bash","label":"yarn"},{"cmd":"pnpm add babel-plugin-transform-typescript-metadata","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required peer dependency for the Babel host environment.","package":"@babel/core","optional":false},{"reason":"Runtime polyfill required by the application to consume the metadata emitted by the plugin. Must be installed separately by the consumer.","package":"reflect-metadata","optional":true}],"imports":[{"note":"This package is a Babel plugin and is configured within your Babel configuration file, typically as a string identifier in the `plugins` array. It does not export symbols for direct `import` or `require` in your application's JavaScript/TypeScript code.","wrong":"import plugin from 'babel-plugin-transform-typescript-metadata'; // This is a Babel plugin, not intended for direct import in application code.\nconst plugin = require('babel-plugin-transform-typescript-metadata'); // Incorrect usage in application code; it's configured by Babel.","symbol":"babel-plugin-transform-typescript-metadata","correct":"// In your Babel configuration file (e.g., .babelrc.js or babel.config.js):\nmodule.exports = {\n  plugins: [\n    \"babel-plugin-transform-typescript-metadata\",\n    // ... other plugins, ensuring this one is first\n  ],\n};\n"}],"quickstart":{"code":"import 'reflect-metadata'; // Essential for runtime metadata access\nimport { injectable, inject } from 'inversify'; // Example: InversifyJS for DI\n\n// Define an interface for clarity\ninterface Logger { \n  log(message: string): void;\n}\n\n// Implement the interface and make it injectable\n@injectable()\nclass ConsoleLogger implements Logger {\n  private readonly prefix: string;\n\n  constructor(@inject('logPrefix') prefix: string) {\n    this.prefix = prefix;\n  }\n\n  log(message: string): void {\n    console.log(`[${this.prefix}] ${message}`);\n  }\n}\n\n// A service that depends on a Logger\n@injectable()\nclass DataService {\n  @inject('Logger') // Property injection example\n  private readonly logger!: Logger;\n\n  private readonly apiUrl: string;\n\n  constructor(@inject('apiUrl') apiUrl: string) { // Constructor injection example\n    this.apiUrl = apiUrl;\n  }\n\n  fetchData(): void {\n    this.logger.log(`Fetching data from ${this.apiUrl}...`);\n    // Simulate fetching data\n    const data = { id: 1, name: 'Sample Data' };\n    this.logger.log(`Data received: ${JSON.stringify(data)}`);\n\n    // Demonstrate emitted metadata (runtime reflection)\n    console.log('\\n--- Runtime Metadata for DataService.logger ---');\n    const loggerType = Reflect.getMetadata('design:type', DataService.prototype, 'logger');\n    console.log('design:type (logger):', loggerType ? loggerType.name : 'unknown');\n\n    console.log('\\n--- Runtime Metadata for ConsoleLogger constructor ---');\n    const constructorParams = Reflect.getMetadata('design:paramtypes', ConsoleLogger);\n    console.log('design:paramtypes (ConsoleLogger):', constructorParams ? constructorParams.map((p: any) => p.name) : 'unknown');\n  }\n}\n\n// To run this, you would typically use a Babel setup:\n// 1. Install dependencies: `npm install --save-dev @babel/core @babel/preset-typescript @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties`\n//    `npm install reflect-metadata inversify`\n// 2. Configure Babel (e.g., in babel.config.js):\n//    module.exports = {\n//      plugins: [\n//        'babel-plugin-transform-typescript-metadata',\n//        ['@babel/plugin-proposal-decorators', { 'legacy': true }],\n//        ['@babel/plugin-proposal-class-properties', { 'loose': true }],\n//      ],\n//      presets: [\n//        '@babel/preset-typescript',\n//      ],\n//    };\n\n// --- Manual setup to run the example without a full Inversify container ---\n// (In a real app, an Inversify container would handle instantiation and injection)\n\n// Mock 'inject' for compile-time type checking and demonstration\nclass FakeInject { constructor(id: string) { return (target: any, key: string, index?: number) => {}; } }\nconst fakeInject = (id: string) => new FakeInject(id) as any;\n\n// Manually create instances and inject for demonstration\nconst logPrefix = 'APP';\nconst consoleLogger: Logger = new ConsoleLogger(logPrefix);\n\nconst apiUrl = 'https://api.example.com/data';\nconst dataService = new DataService(apiUrl);\n\n// Simulate property injection\n(dataService as any).logger = consoleLogger;\n\ndataService.fetchData();\n","lang":"typescript","description":"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`."},"warnings":[{"fix":"Review your `.babelrc` or `babel.config.js` and ensure `'babel-plugin-transform-typescript-metadata'` is the first plugin listed.","message":"The `babel-plugin-transform-typescript-metadata` plugin MUST be placed BEFORE `@babel/plugin-proposal-decorators` in your Babel configuration's `plugins` array. Incorrect ordering will result in the decorator metadata not being emitted, leading to runtime errors in libraries that depend on it.","severity":"breaking","affected_versions":">=0.1.0"},{"fix":"Install `reflect-metadata` and add `import 'reflect-metadata';` as the very first line in your application's main entry file (e.g., `src/main.ts` or `src/index.ts`).","message":"The `reflect-metadata` polyfill is a runtime requirement for applications consuming the metadata emitted by this plugin. It is not a direct dependency of the plugin and must be installed separately (`npm install reflect-metadata`) and imported once, typically at the entry point of your application (e.g., `import 'reflect-metadata';`).","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Update your Babel configuration for the decorators plugin to `['@babel/plugin-proposal-decorators', { 'legacy': true }]`.","message":"To ensure proper parsing and transformation of decorators when using this plugin, `@babel/plugin-proposal-decorators` must be configured with the `{'legacy': true}` option. Failing to do so can lead to syntax errors or incorrect decorator behavior.","severity":"breaking","affected_versions":">=0.1.0"},{"fix":"For Babel 8 projects, use `babel-plugin-transform-typescript-metadata@0.4.0` or newer. For Babel 7 projects, pin to `babel-plugin-transform-typescript-metadata@0.3.x`.","message":"Version `0.4.0` introduced compatibility with Babel 8. If your project uses Babel 7, you should ensure your `@babel/core` peer dependency is compatible and consider using an earlier version of this plugin (e.g., `0.3.x`) to avoid potential compatibility issues.","severity":"breaking","affected_versions":"<0.4.0"},{"fix":"Consult the 'Usage with InversifyJS' section in the plugin's README for specific workarounds and helper functions required for property injection.","message":"When implementing property injection with certain dependency injection libraries (like InversifyJS), Babel's decorator transformation might behave differently than TypeScript's native compilation. This can lead to runtime errors requiring specific helper functions (e.g., `fixPropertyDecorator` documented in the README) to ensure compatibility.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"In your Babel config, ensure `babel-plugin-transform-typescript-metadata` is the first plugin listed, and `@babel/plugin-proposal-decorators` is configured as `['@babel/plugin-proposal-decorators', { 'legacy': true }]`.","cause":"The `babel-plugin-transform-typescript-metadata` is positioned incorrectly (after `@babel/plugin-proposal-decorators`) or `@babel/plugin-proposal-decorators` is missing `{'legacy': true}`.","error":"Error: Decorators are not valid here. This error usually occurs when the decorator plugin is not configured correctly or is placed in the wrong order."},{"fix":"Install `reflect-metadata` (`npm install reflect-metadata`) and add `import 'reflect-metadata';` as the very first line in your application's main entry file (e.g., `src/main.ts`).","cause":"The `reflect-metadata` polyfill, which provides the `Reflect.getMetadata` function, has not been loaded at runtime.","error":"TypeError: Reflect.getMetadata is not a function"},{"fix":"Verify that `reflect-metadata` is imported at the application's entry point and that the Babel plugin (`babel-plugin-transform-typescript-metadata`) is correctly configured (especially plugin order and `legacy: true` for decorators).","cause":"This often indicates that the design-time metadata for a decorated class or property is missing or incorrect, typically because `reflect-metadata` was not loaded, or the Babel plugin failed to emit the metadata due to misconfiguration.","error":"TypeError: Cannot read properties of undefined (reading 'constructor') or similar runtime errors when using decorated classes/properties."}],"ecosystem":"npm"}