Dependency Injection for JavaScript & TypeScript
injection-js is a lightweight (5.2KB minified) dependency injection library specifically designed for JavaScript and TypeScript applications outside of the Angular framework. Extracted from Angular's pre-v5 dependency injection system, it retains its feature completeness, speed, reliability, and robust testing. The library is currently at version 2.6.1 and provides a runtime reflection-based DI solution, differentiating itself from Angular's modern compile-time `StaticInjector`. It is ideal for Node.js, Vue, React, or vanilla JS/TS projects that require a sophisticated DI system without the full Angular ecosystem. Key requirements include a Reflect API polyfill (e.g., `reflect-metadata`) and specific `tsconfig.json` flags for TypeScript decorator support.
Common errors
-
TypeError: Reflect.getMetadata is not a function
cause The required Reflect API polyfill (`reflect-metadata` or similar) has not been imported or is not available at runtime when decorated classes are processed.fixInstall `reflect-metadata` (`npm install reflect-metadata`) and add `import 'reflect-metadata';` as the very first line in your application's main entry point file. This ensures the polyfill loads before any decorated classes are defined. -
Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option to remove this warning.
cause TypeScript compiler is encountering decorator syntax (`@Injectable`) but the `experimentalDecorators` flag is not enabled in `tsconfig.json`.fixOpen your `tsconfig.json` file and add or set `"experimentalDecorators": true` within the `compilerOptions` section. You will likely also need `"emitDecoratorMetadata": true` for `injection-js` to function correctly with decorators.
Warnings
- gotcha Using `injection-js` with TypeScript decorators (`@Injectable`) requires a Reflect API polyfill to be present in your environment. Without it, runtime errors will occur when the injector tries to resolve types.
- gotcha When using TypeScript decorators like `@Injectable()` for dependency injection, you must enable `experimentalDecorators` and `emitDecoratorMetadata` in your `tsconfig.json` compiler options.
- breaking This library is an extraction of Angular's *pre-v5* dependency injection system (`ReflectiveInjector`). It is fundamentally different from Angular v5+ `StaticInjector`, which uses compile-time DI. Do not expect compatibility with newer Angular DI patterns or the Angular compiler.
- gotcha While `injection-js` supports ES6 and ES5 syntax for defining dependencies, the modern TypeScript-first approach using decorators or the `inject` function is recommended for clarity and type safety. Older patterns using `static get parameters()` or `di.Class` might be harder to maintain.
Install
-
npm install injection-js -
yarn add injection-js -
pnpm add injection-js
Imports
- inject
const { inject } = require('injection-js');import { inject } from 'injection-js'; - ReflectiveInjector
const ReflectiveInjector = require('injection-js').ReflectiveInjector;import { ReflectiveInjector } from 'injection-js'; - Injectable
import Injectable from 'injection-js/Injectable';
import { Injectable } from 'injection-js';
Quickstart
import 'reflect-metadata'; // Must be imported once at the application entry point
import { inject, ReflectiveInjector, Injectable } from 'injection-js';
@Injectable()
class HttpService {
public readonly baseUrl = 'https://api.example.com';
constructor() {
console.log('HttpService initialized');
}
}
@Injectable()
class LoggerService {
log(message: string): void {
console.log(`[Log] ${message}`);
}
}
@Injectable()
class DataService {
private http = inject(HttpService);
private logger = inject(LoggerService);
constructor() {
this.logger.log('DataService created, ready for operations.');
}
fetchData(): string {
this.logger.log(`Fetching data from: ${this.http.baseUrl}/data`);
return `Data from ${this.http.baseUrl} for the current user.`;
}
}
// Create an injector with providers
const appInjector = ReflectiveInjector.resolveAndCreate([
HttpService,
LoggerService,
DataService
]);
// Get an instance of DataService from the injector
const dataService = appInjector.get(DataService);
console.log('Is dataService an instance of DataService?', dataService instanceof DataService);
console.log('Fetched:', dataService.fetchData());
// Demonstrate getting another service directly
const loggerService = appInjector.get(LoggerService);
loggerService.log('Application started successfully.');