@tdi2/di-core API Reference
The @tdi2/di-core package provides the foundational dependency injection system for TDI2, including decorators, container management, and React integration.
Installation
Section titled “Installation”npm install @tdi2/di-core# orbun add @tdi2/di-coreCore Decorators
Section titled “Core Decorators”@Service()
Section titled “@Service()”Marks a class as injectable and available for dependency injection.
import { Service } from '@tdi2/di-core';
@Service()export class ProductService implements ProductServiceInterface { state = { products: [] as Product[], loading: false };
async loadProducts(): Promise<void> { this.state.loading = true; // Implementation }}Options:
interface ServiceOptions { token?: string | symbol; // Optional explicit token scope?: 'singleton' | 'transient' | 'scoped'; // Lifecycle scope profiles?: string[]; // Environment profiles}Usage with Options:
@Service({ token: 'CUSTOM_TOKEN', scope: 'transient'})export class TransientService {}@Inject()
Section titled “@Inject()”Marks constructor parameters for dependency injection.
@Service()export class OrderService { constructor( @Inject() private productService: ProductServiceInterface, @Inject() private userService: UserServiceInterface, @Inject('PAYMENT_CONFIG') private config: PaymentConfig ) {}}Automatic Interface Resolution:
// Token automatically resolved from interface type@Service()export class CartService { constructor( private productService: Inject<ProductServiceInterface>, private inventoryService: Inject<InventoryServiceInterface> ) {}}@Scope()
Section titled “@Scope()”Defines the lifecycle scope for a service (follows Spring Boot convention).
import { Service, Scope } from '@tdi2/di-core';
@Service()@Scope('singleton')export class ConfigService {} // Same instance everywhere
@Service()@Scope('transient')export class FormService {} // New instance each injection
@Service()@Scope('scoped')export class RequestService {} // Scoped to DI container contextAvailable Scopes:
singleton- One instance per container (default)transient- New instance for each injectionscoped- Scoped to container lifecycle
@Profile()
Section titled “@Profile()”Marks services for specific environments or configurations.
import { Service, Profile } from '@tdi2/di-core';
@Service()@Profile('development', 'test')export class MockPaymentService implements PaymentServiceInterface {}
@Service()@Profile('production')export class StripePaymentService implements PaymentServiceInterface {}Environment Activation:
// Via environment variables// TDI2_PROFILES=production,logging// ACTIVE_PROFILES=dev,test
// Via container optionsconst container = new CompileTimeDIContainer(undefined, { activeProfiles: ['development', 'external-services']});
// Programmaticallycontainer.setActiveProfiles(['production', 'monitoring']);Profile Negation:
@Service()@Profile('!production') // Load everywhere except productionexport class DebugService {}@Configuration()
Section titled “@Configuration()”Marks a class as a configuration provider that contains @Bean methods for external library integration.
import { Configuration, Bean, Profile } from '@tdi2/di-core';
@Configurationexport class ExternalLibraryConfig { // Configuration methods with @Bean decorators}
@Configuration@Profile('external-services') // Only load when profile is activeexport class ExternalServicesConfig { // External service configurations}Options:
interface ConfigurationOptions { profiles?: string[]; // Environment profiles for this configuration priority?: number; // Loading priority (higher loads first)}@Bean()
Section titled “@Bean()”Marks methods within @Configuration classes as factory functions for external services. Bean methods automatically integrate with the DI system and support all existing decorators.
import { Configuration, Bean, Primary, Scope, Qualifier } from '@tdi2/di-core';
@Configurationexport class DatabaseConfig { @Bean @Primary database(): DatabaseInterface { return new PostgresDatabase({ host: 'localhost', port: 5432 }); }
@Bean @Qualifier('readOnlyDatabase') @Scope('singleton') readOnlyDatabase(): DatabaseInterface { return new PostgresDatabase({ host: 'localhost', port: 5432, readOnly: true }); }
@Bean @Profile('production') prodDatabase(@Inject httpClient: HttpClientInterface): DatabaseInterface { // Dependencies are automatically injected into @Bean methods return new CloudDatabase({ client: httpClient, region: 'us-east-1' }); }}Key Features:
- Automatic Interface Resolution: Return type determines the service interface
- Dependency Injection: Parameters are automatically injected using @Inject
- Decorator Support: Works with @Primary, @Scope, @Qualifier, @Profile
- Profile Inheritance: Inherits configuration profiles unless overridden
- Clean Architecture: No string tokens needed, uses existing decorator system
Usage with Qualifiers:
@Configurationexport class LoggingConfig { @Bean @Primary consoleLogger(): LoggerInterface { return new ConsoleLogger(); }
@Bean @Qualifier('fileLogger') fileLogger(): LoggerInterface { return new FileLogger('/var/log/app.log'); }
@Bean @Qualifier('remoteLogger') @Profile('production') remoteLogger(@Inject emailService: EmailServiceInterface): LoggerInterface { return new RemoteLogger({ emailService }); }}
// In services, inject specific implementations@Service()export class UserService { constructor( @Inject logger: LoggerInterface, // Gets primary (console) @Inject @Qualifier('fileLogger') fileLogger: LoggerInterface // Gets file logger ) {}}@Optional()
Section titled “@Optional()”Marks an injection as optional (won’t throw if dependency is missing).
@Service()export class EmailService { constructor( @Inject() private smtpConfig: SMTPConfig, @Inject() @Optional() private logger?: LoggerService ) {}}@Autowired
Section titled “@Autowired”Alias for @Inject() providing Spring Boot familiarity.
import { Service, Autowired } from '@tdi2/di-core';
@Service()export class UserService { constructor( @Autowired private httpClient: HttpClient, @Autowired private cacheService: CacheService ) {}}Container API
Section titled “Container API”CompileTimeDIContainer
Section titled “CompileTimeDIContainer”The main dependency injection container that manages service registration and resolution.
import { CompileTimeDIContainer } from '@tdi2/di-core';
// Basic containerconst container = new CompileTimeDIContainer();
// Container with optionsconst container = new CompileTimeDIContainer(undefined, { verbose: true, activeProfiles: ['development', 'external-services']});Methods
Section titled “Methods”register
Registers a service with the container.
// Register with explicit tokencontainer.register('USER_SERVICE', UserService);
// Register with scopecontainer.register('FORM_SERVICE', FormService, 'transient');
// Register factory functioncontainer.register('CONFIG', () => ({ apiUrl: process.env.API_URL, timeout: 5000}));get
Retrieves a service instance from the container.
const userService = container.get<UserServiceInterface>('USER_SERVICE');const config = container.get<AppConfig>('CONFIG');has(token)
Checks if a service is registered.
if (container.has('OPTIONAL_SERVICE')) { const service = container.get('OPTIONAL_SERVICE');}createScope()
Creates a new scoped container for managing scoped services.
const requestScope = container.createScope();// Services with @Scope('scoped') will be bound to this scopeloadConfiguration(config)
Loads service configuration from DI configuration object.
import { DI_CONFIG } from './di-config';
container.loadConfiguration(DI_CONFIG);loadContainerConfiguration(config)
Loads full container configuration including profiles and configuration classes.
const config = { diMap: DI_CONFIG, interfaceMapping: INTERFACE_MAPPING, configurations: CONFIGURATION_CLASSES, profiles: ['production', 'external-services']};
container.loadContainerConfiguration(config);setActiveProfiles(profiles)
Sets the active profiles for the container.
container.setActiveProfiles(['development', 'testing']);// Only services with matching profiles will be loadedaddActiveProfiles(profiles)
Adds additional profiles to the active set.
container.addActiveProfiles(['debugging']);// Adds to existing active profilesgetActiveProfiles()
Gets the currently active profiles.
const profiles = container.getActiveProfiles();console.log('Active profiles:', profiles);isProfileActive(profile)
Checks if a specific profile is active.
if (container.isProfileActive('production')) { // Production-specific logic}React Integration
Section titled “React Integration”DIProvider
Section titled “DIProvider”React context provider that makes the DI container available to components.
import { DIProvider, CompileTimeDIContainer } from '@tdi2/di-core';import { DI_CONFIG } from './di-config';
function App() { const container = new CompileTimeDIContainer(); container.loadConfiguration(DI_CONFIG);
return ( <DIProvider container={container}> <MainComponent /> </DIProvider> );}Props:
container: DIContainer- The DI container instancechildren: ReactNode- Child components
useService Hook
Section titled “useService Hook”React hook for accessing services in functional components.
import { useService } from '@tdi2/di-core';
function ProductList() { const productService = useService<ProductServiceInterface>('PRODUCT_SERVICE');
return ( <div> {productService.state.products.map(product => ( <ProductCard key={product.id} product={product} /> ))} </div> );}Lifecycle Interfaces
Section titled “Lifecycle Interfaces”Services can implement lifecycle interfaces for automatic lifecycle management.
OnMount / OnUnmount
Section titled “OnMount / OnUnmount”For React component-like lifecycle (when service is first used/released).
import { Service, OnMount, OnUnmount } from '@tdi2/di-core';
@Service()export class WebSocketService implements OnMount, OnUnmount { private socket: WebSocket;
onMount(): void { this.socket = new WebSocket('ws://localhost:8080'); console.log('WebSocket service mounted'); }
onUnmount(): void { this.socket?.close(); console.log('WebSocket service unmounted'); }}OnInit / OnDestroy
Section titled “OnInit / OnDestroy”For service initialization and cleanup.
@Service()export class DatabaseService implements OnInit, OnDestroy { private connection: DatabaseConnection;
onInit(): void { this.connection = new DatabaseConnection(); console.log('Database service initialized'); }
onDestroy(): void { this.connection?.close(); console.log('Database service destroyed'); }}Type Definitions
Section titled “Type Definitions”Core Types
Section titled “Core Types”// Service factory function typetype ServiceFactory<T> = (container: DIContainer) => T;
// DI container interfaceinterface DIContainer { register<T>(token: string | symbol, implementation: any, scope?: string): void; resolve<T>(token: string | symbol): T; has(token: string | symbol): boolean; createScope(): DIContainer; loadConfiguration(config: DIMap): void; loadContainerConfiguration(config: ContainerConfiguration): void; setActiveProfiles(profiles: string[]): void; addActiveProfiles(profiles: string[]): void; getActiveProfiles(): string[]; isProfileActive(profile: string): boolean;}
// Container optionsinterface DIContainerOptions { verbose?: boolean; activeProfiles?: string[];}
// Service options for @Service decoratorinterface ServiceOptions { token?: string | symbol; scope?: 'singleton' | 'transient' | 'scoped'; profiles?: string[]; primary?: boolean; qualifier?: string;}
// Configuration options for @Configuration decoratorinterface ConfigurationOptions { profiles?: string[]; priority?: number;}
// Full container configurationinterface ContainerConfiguration { diMap: DIMap; interfaceMapping: InterfaceMapping; configurations: ConfigurationMetadata[]; profiles?: string[]; environment?: string;}
// Configuration class metadatainterface ConfigurationMetadata { className: string; filePath: string; profiles: string[]; priority: number; beans: BeanMetadata[];}
// Bean method metadatainterface BeanMetadata { methodName: string | symbol; returnType: string; parameters: BeanParameterMetadata[]; scope: 'singleton' | 'transient' | 'scoped'; primary: boolean; qualifier?: string; autoResolve: boolean; profiles?: string[];}
// Bean parameter metadatainterface BeanParameterMetadata { parameterName: string; parameterType: string; isOptional: boolean; qualifier?: string;}Marker Types
Section titled “Marker Types”For functional component injection:
// Inject marker for functional componentstype Inject<T> = T;
// Service props patterninterface ServicesProps { productService: Inject<ProductServiceInterface>; cartService: Inject<CartServiceInterface>;}
// Component with service injectionfunction ShoppingCart({ productService, cartService }: ServicesProps) { // Implementation}Advanced Usage
Section titled “Advanced Usage”Configuration-Based External Libraries
Section titled “Configuration-Based External Libraries”Use @Configuration and @Bean to wrap external libraries:
@Configuration@Profile('external-services')export class ExternalLibraryConfig { @Bean @Primary httpClient(): HttpClientInterface { return new AxiosHttpClient({ timeout: 5000, retries: 3 }); }
@Bean @Qualifier('fileLogger') @Profile('production') prodLogger(): LoggerInterface { return new WinstonLogger({ level: 'error', filename: '/var/log/app.log' }); }
@Bean redisCache(@Inject config: ConfigServiceInterface): CacheInterface { return new RedisCache({ host: config.redis.host, port: config.redis.port }); }}
// Usage in services@Service()export class UserService { constructor( @Inject private httpClient: HttpClientInterface, // Gets AxiosHttpClient @Inject @Qualifier('fileLogger') private logger: LoggerInterface // Gets WinstonLogger in prod ) {}}Custom Service Factories
Section titled “Custom Service Factories”Register complex service creation logic:
container.register('DATABASE', (container) => { const config = container.get<DatabaseConfig>('DATABASE_CONFIG'); return new DatabaseService({ host: config.host, port: config.port, credentials: container.get<Credentials>('DB_CREDENTIALS') });});Conditional Registration
Section titled “Conditional Registration”Register different implementations based on environment:
if (process.env.NODE_ENV === 'production') { container.register('PAYMENT_SERVICE', StripePaymentService);} else { container.register('PAYMENT_SERVICE', MockPaymentService);}Service Hierarchies
Section titled “Service Hierarchies”Create child containers with inherited services:
const globalContainer = new CompileTimeDIContainer();globalContainer.register('GLOBAL_CONFIG', GlobalConfig);
const moduleContainer = new CompileTimeDIContainer(globalContainer);moduleContainer.register('MODULE_SERVICE', ModuleService);
// ModuleService can access GLOBAL_CONFIG through parent containerError Handling
Section titled “Error Handling”Common Errors
Section titled “Common Errors”ServiceNotFoundError
try { const service = container.get('NONEXISTENT_SERVICE');} catch (error) { if (error.name === 'ServiceNotFoundError') { console.error('Service not registered:', error.token); }}CircularDependencyError
// TDI2 detects circular dependencies at compile time// Error: Circular dependency detected: A -> B -> AScopeError
// Error: Cannot inject transient service into singleton// Solution: Use factory pattern or adjust scopesBest Practices
Section titled “Best Practices”1. Use Interface-Based Injection
Section titled “1. Use Interface-Based Injection”// Good: Interface-based@Service()export class EmailService implements EmailServiceInterface { async sendEmail(to: string, subject: string): Promise<void> {}}
// Usageconstructor(private emailService: Inject<EmailServiceInterface>) {}2. Explicit Service Boundaries
Section titled “2. Explicit Service Boundaries”// Good: Clear service responsibility@Service()export class OrderService { constructor( private paymentService: Inject<PaymentServiceInterface>, private inventoryService: Inject<InventoryServiceInterface>, private notificationService: Inject<NotificationServiceInterface> ) {}}3. Proper Scope Management
Section titled “3. Proper Scope Management”// Stateless services: singleton (default)@Service()export class UtilityService {}
// Stateful per-request services: transient@Service()@Scope('transient')export class FormService {}4. Profile-Based Configuration
Section titled “4. Profile-Based Configuration”@Service()@Profile('test')export class MockEmailService implements EmailServiceInterface { async sendEmail(): Promise<void> { console.log('Mock email sent'); }}
@Service()@Profile('production')export class SMTPEmailService implements EmailServiceInterface { async sendEmail(): Promise<void> { // Real SMTP implementation }}5. Configuration Classes for External Libraries
Section titled “5. Configuration Classes for External Libraries”// Good: Clean separation of external library configuration@Configuration@Profile('external-services')export class ExternalLibConfig { @Bean @Primary httpClient(): HttpClientInterface { return new AxiosHttpClient({ timeout: 5000 }); }
@Bean @Qualifier('authClient') authHttpClient(@Inject config: AuthConfigInterface): HttpClientInterface { return new AxiosHttpClient({ baseURL: config.authUrl, headers: { 'Authorization': `Bearer ${config.token}` } }); }}
// Usage: Clean dependency injection@Service()export class UserService { constructor( @Inject private httpClient: HttpClientInterface, @Inject @Qualifier('authClient') private authClient: HttpClientInterface ) {}}6. Environment-Aware Services
Section titled “6. Environment-Aware Services”// Good: Environment-specific implementations@Configurationexport class DatabaseConfig { @Bean @Profile('development', 'test') devDatabase(): DatabaseInterface { return new SQLiteDatabase(':memory:'); }
@Bean @Profile('production') prodDatabase(@Inject config: ConfigServiceInterface): DatabaseInterface { return new PostgresDatabase({ host: config.database.host, credentials: config.database.credentials }); }}Migration from Other DI Systems
Section titled “Migration from Other DI Systems”From Manual Dependency Management
Section titled “From Manual Dependency Management”// Before: Manual instantiationconst apiClient = new ApiClient();const userService = new UserService(apiClient);const cartService = new CartService(apiClient, userService);
// After: DI containerconst container = new CompileTimeDIContainer();container.loadConfiguration(DI_CONFIG);// All dependencies resolved automaticallyFrom React Context
Section titled “From React Context”// Before: Context providers<ApiProvider> <UserProvider> <CartProvider> <App /> </CartProvider> </UserProvider></ApiProvider>
// After: Single DI provider<DIProvider container={container}> <App /></DIProvider>This API reference covers all major features of @tdi2/di-core. For complete examples and integration patterns, see the Quick Start Guide and E-Commerce Case Study.