Powerful, type-safe dependency injection container for TypeScript
Getting Started · Core Concepts · API Reference
A modern, feature-rich Dependency Injection container for TypeScript applications. Built with developer experience in mind, featuring decorators, modules, interceptors, and comprehensive testing utilities.
- Full DI Support - Constructor injection, property injection, and factory providers
- Type-Safe - Leverages TypeScript for compile-time safety with
InjectionToken<T> - 5 Provider Types - Class, Value, Factory, Existing (alias), and Async providers
- 5 Scopes - Singleton, Transient, Request, Prototype, and Scoped
- Decorator-Based -
@Injectable,@Inject,@Optional,@Lazy - Module System - Organize code with
@Module, dynamic modules, and configurable modules - Interceptors - Built-in caching, logging, retry, and validation interceptors
- Testing Utilities -
TestContainerwith mock, spy, and snapshot support - AOP Decorators -
@Cached,@Log,@Transactionalfor cross-cutting concerns - Lifecycle Hooks -
onInit,onDestroyfor resource management - Hierarchical Containers - Parent/child container relationships
- Cycle Detection - Automatic circular dependency detection with Tarjan's algorithm
- Zero Dependencies - Only requires
reflect-metadataas peer dependency
npm install @noneforge/ioc reflect-metadataRequired peer dependencies:
reflect-metadata>= 0.2.0typescript>= 5.0
Add to your tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Import reflect-metadata once at your application entry point:
import'reflect-metadata';import'reflect-metadata';import{Container,Injectable,inject,InjectionToken}from'@noneforge/ioc';// Define a service
@Injectable()classLoggerService{log(message: string){console.log(`[LOG] ${message}`);}}// Define another service with dependency using inject()
@Injectable()classUserService{privatelogger=inject(LoggerService);createUser(name: string){this.logger.log(`Creating user: ${name}`);return{id: 1, name };}}// Create container and resolveconstcontainer=newContainer();container.addProvider(LoggerService);container.addProvider(UserService);constuserService=container.get(UserService);userService.createUser('John');// [LOG] Creating user: Johnimport{InjectionToken,Container}from'@noneforge/ioc';// Create type-safe tokensconstAPI_URL=newInjectionToken<string>('API_URL');constMAX_RETRIES=newInjectionToken<number>('MAX_RETRIES');constcontainer=newContainer();// Register valuescontainer.addProvider({provide: API_URL,useValue: 'https://api.example.com'});container.addProvider({provide: MAX_RETRIES,useValue: 3});// Resolve with type safetyconstapiUrl=container.get(API_URL);// stringconstmaxRetries=container.get(MAX_RETRIES);// numberimport{Module,Injectable,inject,InjectionToken,bootstrap}from'@noneforge/ioc';constCONFIG=newInjectionToken<{apiUrl: string}>('CONFIG');
@Injectable()classApiService{privateconfig=inject(CONFIG);getUrl(){returnthis.config.apiUrl;}}
@Module({providers: [{provide: CONFIG,useValue: {apiUrl: 'https://api.example.com'}},ApiService,],exports: [ApiService],})classApiModule{}
@Module({imports: [ApiModule],})classAppModule{}// Bootstrap the applicationconst{ app, container }=awaitbootstrap(AppModule);constapiService=container.get(ApiService);import{createTestContainer,createMockProvider}from'@noneforge/ioc';// Create test container with mocksconstcontainer=createTestContainer(createMockProvider(LoggerService,{log: vi.fn(),}),UserService,);constuserService=container.get(UserService);userService.createUser('Test');// Verify mock was calledexpect(container.get(LoggerService).log).toHaveBeenCalledWith('Creating user: Test');// Class Provider - instantiate a class{provide: MyService,useClass: MyService}// Value Provider - use a constant value{provide: API_KEY,useValue: 'secret-key'}// Factory Provider - create with dependencies{provide: DatabaseConnection,useFactory: (config: Config)=>newDatabase(config.dbUrl),inject: [Config]}// Existing Provider - alias to another token{provide: ILogger,useExisting: ConsoleLogger}// Async Provider - async initialization{provide: RemoteConfig,useAsync: ()=>fetch('/config').then(r=>r.json())}@Injectable({scope: 'singleton'})// Default - one instance per containerclassSingletonService{}
@Injectable({scope: 'transient'})// New instance every timeclassTransientService{}
@Injectable({scope: 'request'})// One instance per request IDclassRequestScopedService{}inject(Token)// Resolve dependencyinjectOptional(Token)// Allow null if not foundinjectLazy(Token)// Defer resolution until first accessinjectAll(Token)// Get all multi-providers@Injectable()// Mark class as injectable
@Inject(Token)// Specify injection token (alternative to inject())
@Optional()// Allow null if not found (alternative to injectOptional())
@Lazy()// Defer resolution (alternative to injectLazy())- Getting Started - Installation and first steps
- Core Concepts - Tokens, containers, and resolution
- Providers - All provider types explained
- Scopes - Lifecycle management
- Decorators - Injectable, Inject, Optional, Lazy
- Modules - Module system and composition
- Interceptors - Built-in and custom interceptors
- Testing - TestContainer and mocking
- Advanced - Lifecycle hooks, cache, plugins
- API Reference - Complete API documentation
| Feature | @noneforge/ioc | InversifyJS | TSyringe | TypeDI |
|---|---|---|---|---|
| Decorator-based DI | Yes | Yes | Yes | Yes |
| Injection Tokens | Yes | Yes | Yes | Yes |
| Module System | Yes | Yes | No | No |
| Built-in Interceptors | Yes | Yes | Yes | No |
| Testing Utilities | Yes | No | No | No |
| AOP Decorators | Yes | No | No | No |
| Cycle Detection | Yes | Yes | Yes | No |
| Dependency Graph | Yes | No | No | No |
| Request Scope | Yes | Yes | Yes | Yes |
| Async Providers | Yes | Yes | Yes | Yes |
| Conditional Providers | Yes | Yes | No | No |
| EnhancedCache (LRU/LFU) | Yes | No | No | No |
See the examples directory for runnable code samples:
- Basic DI - Simple dependency injection
- Modules - Module system usage
- Interceptors - Using interceptors
- Scopes - Scope management
- Testing - Testing patterns
- Node.js >= 18.18.0
- TypeScript >= 5.0
reflect-metadata>= 0.2.0
Contributions are welcome! Please read the contributing guidelines before submitting a pull request.
MIT License - see LICENSE for details.