Typed Inject
Typed Inject is a robust, 100% type-safe dependency injection framework specifically designed for TypeScript applications. It allows developers to inject classes, interfaces, or primitive values, leveraging TypeScript's type system to ensure that if a project compiles, its dependencies are correctly resolved at runtime with their declared types. The current stable version is 5.0.0, released in late 2024. Major versions typically roll out with significant changes like Node.js version updates or module system migrations, as seen with the v4.0.0 (ESM migration) and v5.0.0 (Node 16 drop) releases. Its primary differentiator is its strong type-safety, which virtually eliminates runtime dependency resolution errors by shifting validation to compile time. It handles various injection patterns, including class, value, and factory providers, and supports concepts like child injectors and lifecycle control.
Common errors
-
TypeError: createInjector is not a function
cause Attempting to use CommonJS `require()` syntax after typed-inject migrated to native ES Modules in v4.0.0.fixChange `const { createInjector } = require('typed-inject');` to `import { createInjector } from 'typed-inject';` and ensure your project is configured for ESM. -
Error: Could not resolve dependency for token "someDependency"
cause A required dependency was not provided to the injector, or the injection token name does not match the provided name.fixVerify that `appInjector.provideValue`, `provideClass`, or `provideFactory` methods are called for all required dependencies (e.g., `appInjector.provideValue('someDependency', instance);`) and that the token names match exactly. -
TS2345: Argument of type 'typeof MyClass' is not assignable to parameter of type 'new (...args: any[]) => MyClass'.
cause Likely due to missing or incorrect `static inject` property on the class being injected, or a mismatch between the provided dependency types and the constructor's expected types.fixEnsure the class has a `public static inject = ['dependencyName1', 'dependencyName2'] as const;` property, and that the names listed correspond to correctly provided dependencies and match the constructor's parameters in order and type.
Warnings
- breaking Version 5.0.0 of typed-inject dropped official support for Node.js 16. Projects must use Node.js 18 or newer.
- breaking Version 4.0.0 of typed-inject migrated to native ESM. CommonJS `require` statements will no longer work and may lead to runtime errors.
- breaking Version 4.0.0 of typed-inject dropped official support for Node.js 14. Projects must use Node.js 16 or newer.
- gotcha typed-inject requires TypeScript version 3.0 or above. Additionally, a known bug in TypeScript versions >3.8 and <4.5 might prevent some type errors from being caught.
- gotcha To ensure full type-safety and proper error catching, `--strictFunctionTypes` (or `--strict`) must be enabled in your TypeScript configuration.
Install
-
npm install typed-inject -
yarn add typed-inject -
pnpm add typed-inject
Imports
- createInjector
const { createInjector } = require('typed-inject');import { createInjector } from 'typed-inject'; - Injector
import { Injector } from 'typed-inject';import type { Injector } from 'typed-inject'; - InjectionToken
import type { InjectionToken } from 'typed-inject';
Quickstart
import { createInjector } from 'typed-inject';
interface Logger {
info(message: string): void;
}
const logger: Logger = {
info(message: string) {
console.log(`[Logger]: ${message}`);
},
};
class HttpClient {
constructor(private log: Logger) {}
public static inject = ['logger'] as const;
fetchData(url: string) {
this.log.info(`Fetching data from ${url}`);
return Promise.resolve(`Data from ${url}`);
}
}
class MyService {
constructor(
private http: HttpClient,
private log: Logger,
) {}
public static inject = ['httpClient', 'logger'] as const;
async doSomething() {
this.log.info('MyService doing something...');
const data = await this.http.fetchData('https://example.com/api/data');
this.log.info(`Received: ${data}`);
return data;
}
}
const appInjector = createInjector()
.provideValue('logger', logger)
.provideClass('httpClient', HttpClient);
// Create an instance of MyService with all dependencies resolved
const myService = appInjector.injectClass(MyService);
myService.doSomething();
// This demonstrates setting up an injector, providing a value and a class, and then injecting a dependent class.