A library for constructing Web Components
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Full-featured dependency injection container with decorators, service registration, and hierarchical resolution for building modular and testable applications.
Core container for managing dependency registration, resolution, and lifetime management with hierarchical scoping support.
/**
* Central DI container interface
*/
interface Container extends ServiceLocator {
/**
* Gets an instance of a dependency
* @param key - The key that identifies the dependency
*/
get<K extends Key>(key: K): Resolved<K>;
/**
* Gets an instance of a dependency asynchronously
* @param key - The key that identifies the dependency
*/
getAsync<K extends Key>(key: K): Promise<Resolved<K>>;
/**
* Gets all instances of a dependency
* @param key - The key that identifies the dependency
* @param searchAncestors - Whether to search ancestor containers
*/
getAll<K extends Key>(key: K, searchAncestors?: boolean): readonly Resolved<K>[];
/**
* Registers dependencies with the container
* @param registrations - The registrations to add
*/
register(...registrations: Registration[]): Container;
/**
* Registers a resolver for a specific key
* @param key - The key to register the resolver for
* @param resolver - The resolver to register
*/
registerResolver<K extends Key>(key: K, resolver: Resolver<K>): Resolver<K>;
/**
* Creates a child container
* @param config - Configuration for the child container
*/
createChild(config?: ContainerConfiguration): Container;
/**
* Disposes of the container and its resources
*/
dispose(): void;
}
/**
* Configuration options for containers
*/
interface ContainerConfiguration {
/** Parent container locator */
parentLocator?: ParentLocator;
/** Default resolver to use when no registration is found */
defaultResolver?: (key: Key, handler: Container) => Resolver | null;
/** Whether to inherit registrations from parent */
inheritParentResources?: boolean;
}
/**
* Main DI utilities object
*/
const DI: {
/**
* Creates a new DI container
* @param config - Container configuration
*/
createContainer(config?: ContainerConfiguration): Container;
/**
* Gets or creates a DOM container for an element
* @param node - The DOM node to associate with the container
*/
getOrCreateDOMContainer(node?: Node): DOMContainer;
/**
* Gets the design-time type associated with a key
* @param key - The key to get the type for
*/
getDesignTimeType<K extends Key>(key: K): K | null;
/**
* Gets dependencies for a type
* @param Type - The type to get dependencies for
*/
getDependencies(Type: Constructable): Key[];
/**
* Defines dependencies for a type
* @param Type - The type to define dependencies for
* @param dependencies - The dependencies
*/
defineDependencies(Type: Constructable, dependencies: Key[]): void;
/**
* Gets the factory for a type
* @param Type - The type to get the factory for
*/
getFactory<T extends Constructable>(Type: T): Factory<T>;
};Usage Examples:
import { DI, Container, inject, singleton, transient } from "@microsoft/fast-element/di.js";
// Service interfaces and implementations
interface ILogger {
log(message: string): void;
error(message: string): void;
}
interface IApiClient {
get(url: string): Promise<any>;
post(url: string, data: any): Promise<any>;
}
interface IUserService {
getCurrentUser(): Promise<User>;
updateUser(user: User): Promise<void>;
}
// Service implementations
@singleton()
class ConsoleLogger implements ILogger {
log(message: string): void {
console.log(`[LOG] ${new Date().toISOString()}: ${message}`);
}
error(message: string): void {
console.error(`[ERROR] ${new Date().toISOString()}: ${message}`);
}
}
@singleton()
class HttpApiClient implements IApiClient {
constructor(@inject('baseUrl') private baseUrl: string) {}
async get(url: string): Promise<any> {
const response = await fetch(`${this.baseUrl}${url}`);
return response.json();
}
async post(url: string, data: any): Promise<any> {
const response = await fetch(`${this.baseUrl}${url}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
}
@transient()
class UserService implements IUserService {
constructor(
@inject(IApiClient) private apiClient: IApiClient,
@inject(ILogger) private logger: ILogger
) {}
async getCurrentUser(): Promise<User> {
this.logger.log('Fetching current user');
try {
return await this.apiClient.get('/user/current');
} catch (error) {
this.logger.error(`Failed to fetch user: ${error}`);
throw error;
}
}
async updateUser(user: User): Promise<void> {
this.logger.log(`Updating user ${user.id}`);
try {
await this.apiClient.post('/user/update', user);
} catch (error) {
this.logger.error(`Failed to update user: ${error}`);
throw error;
}
}
}
// Container setup and usage
const container = DI.createContainer();
// Register dependencies
container.register(
// Register interfaces to implementations
Registration.singleton(ILogger, ConsoleLogger),
Registration.singleton(IApiClient, HttpApiClient),
Registration.transient(IUserService, UserService),
// Register configuration values
Registration.instance('baseUrl', 'https://api.example.com'),
Registration.instance('appName', 'My Application')
);
// Resolve and use services
const userService = container.get(IUserService);
const logger = container.get(ILogger);
// Services are now ready to use
logger.log('Application starting');
// Example component using DI
@customElement("di-example")
export class DIExample extends FASTElement {
constructor(
@inject(IUserService) private userService: IUserService,
@inject(ILogger) private logger: ILogger
) {
super();
}
async connectedCallback() {
super.connectedCallback();
try {
const user = await this.userService.getCurrentUser();
this.logger.log(`User ${user.name} connected`);
} catch (error) {
this.logger.error('Failed to load user data');
}
}
static template = html`
<div>Component with injected dependencies</div>
`;
}
// Create child container with additional registrations
const childContainer = container.createChild();
childContainer.register(
Registration.instance('environment', 'development')
);
// Child container inherits parent registrations
const childLogger = childContainer.get(ILogger); // Same instance as parent
const environment = childContainer.get('environment'); // 'development'
interface User {
id: string;
name: string;
email: string;
}Decorators for marking dependencies to be injected and configuring injection behavior.
/**
* Decorator for dependency injection
* @param keys - The keys to inject
*/
function inject(...keys: Key[]): ParameterDecorator | PropertyDecorator;
/**
* Decorator for singleton registration
* @param RegisterTarget - Optional registration target
*/
function singleton<T extends Constructable>(RegisterTarget?: T): RegisterSelf<T>;
/**
* Decorator for transient registration
* @param RegisterTarget - Optional registration target
*/
function transient<T extends Constructable>(RegisterTarget?: T): RegisterSelf<T>;
/**
* Creates a resolver that resolves all instances of a key
* @param key - The key to resolve all instances for
*/
function all<K extends Key>(key: K): Key;
/**
* Creates a lazy resolver that defers resolution
* @param key - The key to resolve lazily
*/
function lazy<K extends Key>(key: K): Key;
/**
* Creates an optional resolver that returns null if not found
* @param key - The key to resolve optionally
*/
function optional<K extends Key>(key: K): Key;
/**
* Marks a parameter to be ignored during injection
*/
function ignore<K extends Key>(key: K): Key;
/**
* Creates a resolver for new instances per scope
* @param key - The key to create new instances for
*/
function newInstanceForScope<K extends Key>(key: K): Key;
/**
* Creates a resolver for new instances of a type
* @param key - The key to create new instances for
*/
function newInstanceOf<K extends Key>(key: K): Key;Usage Examples:
import {
inject,
singleton,
transient,
all,
lazy,
optional,
ignore,
newInstanceForScope,
newInstanceOf,
DI
} from "@microsoft/fast-element";
// Service interfaces
interface IPlugin {
name: string;
initialize(): void;
}
interface INotificationService {
send(message: string): void;
}
interface IConfigService {
get(key: string): any;
}
// Plugin implementations
@transient()
class EmailPlugin implements IPlugin {
name = "Email Plugin";
initialize(): void {
console.log("Email plugin initialized");
}
}
@transient()
class PushPlugin implements IPlugin {
name = "Push Plugin";
initialize(): void {
console.log("Push plugin initialized");
}
}
// Services with different injection patterns
@singleton()
class NotificationService implements INotificationService {
constructor(
// Inject all registered plugins
@inject(all(IPlugin)) private plugins: IPlugin[],
// Optional dependency - won't fail if not registered
@inject(optional(IConfigService)) private config?: IConfigService,
// Lazy injection - resolved when first accessed
@inject(lazy(ILogger)) private getLogger?: () => ILogger
) {}
send(message: string): void {
// Use logger if available (lazy resolution)
const logger = this.getLogger?.();
logger?.log(`Sending notification: ${message}`);
// Initialize all plugins
this.plugins.forEach(plugin => {
plugin.initialize();
});
// Use config if available
const timeout = this.config?.get('notificationTimeout') || 5000;
console.log(`Notification sent with timeout: ${timeout}ms`);
}
}
// Service with ignored parameters
@singleton()
class DatabaseService {
constructor(
@inject('connectionString') private connectionString: string,
@ignore private debugInfo?: string, // Not injected, can be passed manually
@inject(optional(ILogger)) private logger?: ILogger
) {
this.logger?.log(`Database service created with connection: ${connectionString}`);
}
}
// Service with new instance resolvers
@singleton()
class TaskProcessor {
constructor(
// Each task gets a new worker instance
@inject(newInstanceOf(ITaskWorker)) private createWorker: () => ITaskWorker,
// New session per processing scope
@inject(newInstanceForScope(IProcessingSession)) private getSession: () => IProcessingSession,
@inject(ILogger) private logger: ILogger
) {}
async processTask(task: Task): Promise<void> {
const worker = this.createWorker();
const session = this.getSession();
this.logger.log(`Processing task ${task.id} with worker ${worker.id}`);
try {
await worker.execute(task);
session.markCompleted(task.id);
} catch (error) {
session.markFailed(task.id, error);
throw error;
}
}
}
// Component with complex injection
@customElement("complex-di-example")
export class ComplexDIExample extends FASTElement {
private notificationService: INotificationService;
private taskProcessor: TaskProcessor;
private allPlugins: IPlugin[];
constructor(
@inject(INotificationService) notificationService: INotificationService,
@inject(TaskProcessor) taskProcessor: TaskProcessor,
@inject(all(IPlugin)) allPlugins: IPlugin[]
) {
super();
this.notificationService = notificationService;
this.taskProcessor = taskProcessor;
this.allPlugins = allPlugins;
}
connectedCallback() {
super.connectedCallback();
// Use injected services
this.notificationService.send("Component connected");
console.log(`Available plugins: ${this.allPlugins.map(p => p.name).join(', ')}`);
}
async processExampleTask() {
const task: Task = {
id: 'example-task',
data: { message: 'Hello World' },
priority: 'normal'
};
try {
await this.taskProcessor.processTask(task);
this.notificationService.send("Task processed successfully");
} catch (error) {
this.notificationService.send("Task processing failed");
}
}
static template = html<ComplexDIExample>`
<div class="complex-di-demo">
<h2>Complex DI Example</h2>
<p>This component uses various injection patterns</p>
<button @click="${x => x.processExampleTask()}">
Process Task
</button>
</div>
`;
}
// Container setup with all registrations
const container = DI.createContainer();
container.register(
// Register all plugins
Registration.transient(IPlugin, EmailPlugin),
Registration.transient(IPlugin, PushPlugin),
// Register services
Registration.singleton(INotificationService, NotificationService),
Registration.singleton(TaskProcessor, TaskProcessor),
Registration.singleton(DatabaseService, DatabaseService),
// Register configuration
Registration.instance('connectionString', 'mongodb://localhost:27017'),
Registration.instance(IConfigService, {
get: (key: string) => {
const config = { notificationTimeout: 3000, maxRetries: 3 };
return config[key as keyof typeof config];
}
})
);
interface ITaskWorker {
id: string;
execute(task: Task): Promise<void>;
}
interface IProcessingSession {
markCompleted(taskId: string): void;
markFailed(taskId: string, error: any): void;
}
interface Task {
id: string;
data: any;
priority: 'low' | 'normal' | 'high';
}Registration utilities for configuring how services are resolved and their lifetimes.
/**
* Registration utilities for DI container
*/
const Registration: {
/**
* Creates a transient registration (new instance each time)
* @param key - The key to register under
* @param value - The value or type to register
*/
transient<T>(key: Key, value: Constructable<T> | T): Registration<T>;
/**
* Creates a singleton registration (same instance each time)
* @param key - The key to register under
* @param value - The value or type to register
*/
singleton<T>(key: Key, value: Constructable<T> | T): Registration<T>;
/**
* Creates an instance registration (specific instance)
* @param key - The key to register under
* @param value - The instance to register
*/
instance<T>(key: Key, value: T): Registration<T>;
/**
* Creates a callback registration (custom resolution logic)
* @param key - The key to register under
* @param callback - The callback to resolve the dependency
*/
callback<T>(key: Key, callback: ResolveCallback<T>): Registration<T>;
/**
* Creates a cached callback registration
* @param key - The key to register under
* @param callback - The callback to resolve the dependency
*/
cachedCallback<T>(key: Key, callback: ResolveCallback<T>): Registration<T>;
/**
* Creates an aliased registration (redirects to another key)
* @param originalKey - The original key
* @param aliasKey - The alias key
*/
alias<T>(originalKey: Key, aliasKey: Key): Registration<T>;
/**
* Creates a defer registration (resolves when first requested)
* @param key - The key to register under
* @param callback - The callback to create the registration
*/
defer<T>(key: Key, callback: () => Registration<T>): Registration<T>;
};
/**
* Resolver interface for custom resolution logic
*/
interface Resolver<K = any> {
/** Marks this object as a resolver */
readonly $isResolver: true;
/**
* Resolves the dependency
* @param handler - The container handling the resolution
* @param requestor - The container that requested the resolution
*/
resolve(handler: Container, requestor: Container): Resolved<K>;
/**
* Resolves the dependency asynchronously
* @param handler - The container handling the resolution
* @param requestor - The container that requested the resolution
*/
resolveAsync(handler: Container, requestor: Container): Promise<Resolved<K>>;
/**
* Gets the factory for this resolver
* @param container - The container to get the factory from
*/
getFactory?(container: Container): Factory<K> | null;
}Usage Examples:
import { Registration, DI, Container, ResolveCallback } from "@microsoft/fast-element/di.js";
// Service interfaces
interface IDataService {
getData(): Promise<any>;
}
interface ICacheService {
get(key: string): any;
set(key: string, value: any): void;
}
interface IMetricsService {
recordEvent(event: string): void;
}
// Service implementations
class ApiDataService implements IDataService {
constructor(private apiUrl: string) {}
async getData(): Promise<any> {
const response = await fetch(`${this.apiUrl}/data`);
return response.json();
}
}
class MemoryCacheService implements ICacheService {
private cache = new Map<string, any>();
get(key: string): any {
return this.cache.get(key);
}
set(key: string, value: any): void {
this.cache.set(key, value);
}
}
class MetricsService implements IMetricsService {
recordEvent(event: string): void {
console.log(`Metrics: ${event} at ${new Date().toISOString()}`);
}
}
// Container setup with different registration types
const container = DI.createContainer();
// 1. Instance registration - specific instance
const cacheInstance = new MemoryCacheService();
container.register(
Registration.instance(ICacheService, cacheInstance)
);
// 2. Singleton registration - same instance each time
container.register(
Registration.singleton(IMetricsService, MetricsService)
);
// 3. Transient registration - new instance each time
container.register(
Registration.transient(IDataService, ApiDataService)
);
// 4. Callback registration - custom resolution logic
const dataServiceCallback: ResolveCallback<IDataService> = (handler, requestor, resolver) => {
const environment = handler.get('environment');
const apiUrl = environment === 'production'
? 'https://api.prod.example.com'
: 'https://api.dev.example.com';
return new ApiDataService(apiUrl);
};
container.register(
Registration.callback(IDataService, dataServiceCallback)
);
// 5. Cached callback registration - callback result cached
const expensiveServiceCallback: ResolveCallback<IExpensiveService> = (handler, requestor, resolver) => {
console.log('Creating expensive service (this should only happen once)');
return new ExpensiveService();
};
container.register(
Registration.cachedCallback(IExpensiveService, expensiveServiceCallback)
);
// 6. Alias registration - redirect one key to another
container.register(
Registration.alias('cache', ICacheService),
Registration.alias('metrics', IMetricsService)
);
// 7. Defer registration - create registration when needed
container.register(
Registration.defer('conditionalService', () => {
const shouldUseAdvanced = container.get('useAdvancedFeatures');
return shouldUseAdvanced
? Registration.singleton('conditionalService', AdvancedService)
: Registration.singleton('conditionalService', BasicService);
})
);
// Complex registration scenarios
class ServiceFactory {
static createDatabaseRegistrations(connectionString: string): Registration[] {
return [
Registration.instance('connectionString', connectionString),
Registration.callback('dbConnection', (handler, requestor, resolver) => {
const connStr = handler.get('connectionString');
return new DatabaseConnection(connStr);
}),
Registration.singleton('userRepository', UserRepository),
Registration.singleton('productRepository', ProductRepository)
];
}
static createHttpClientRegistrations(baseUrl: string, timeout: number): Registration[] {
return [
Registration.instance('httpConfig', { baseUrl, timeout }),
Registration.cachedCallback('httpClient', (handler, requestor, resolver) => {
const config = handler.get('httpConfig');
const client = new HttpClient(config.baseUrl);
client.timeout = config.timeout;
return client;
}),
Registration.transient('apiService', ApiService)
];
}
}
// Register groups of related services
container.register(
...ServiceFactory.createDatabaseRegistrations('mongodb://localhost:27017'),
...ServiceFactory.createHttpClientRegistrations('https://api.example.com', 5000)
);
// Environment-specific registrations
const environment = process.env.NODE_ENV || 'development';
if (environment === 'development') {
container.register(
Registration.singleton(ILogger, VerboseLogger),
Registration.instance('debugMode', true)
);
} else {
container.register(
Registration.singleton(ILogger, ProductionLogger),
Registration.instance('debugMode', false)
);
}
// Custom resolver example
class DatabasePoolResolver implements Resolver<IDatabasePool> {
readonly $isResolver = true;
private pool?: IDatabasePool;
resolve(handler: Container, requestor: Container): IDatabasePool {
if (!this.pool) {
const connectionString = handler.get('connectionString');
const maxConnections = handler.get('maxConnections') || 10;
this.pool = new DatabasePool(connectionString, maxConnections);
}
return this.pool;
}
async resolveAsync(handler: Container, requestor: Container): Promise<IDatabasePool> {
return this.resolve(handler, requestor);
}
}
// Register custom resolver
container.registerResolver(IDatabasePool, new DatabasePoolResolver());
// Usage examples
const cache = container.get(ICacheService); // Gets the specific instance
const metrics1 = container.get(IMetricsService); // Gets singleton instance
const metrics2 = container.get(IMetricsService); // Same instance as metrics1
const data1 = container.get(IDataService); // Gets new transient instance
const data2 = container.get(IDataService); // Gets different transient instance
const aliasedCache = container.get('cache'); // Same as ICacheService instance
const aliasedMetrics = container.get('metrics'); // Same as IMetricsService instance
// Mock interfaces for examples
interface IExpensiveService {
performCalculation(): number;
}
interface IDatabasePool {
getConnection(): Promise<any>;
}
interface ILogger {
log(message: string): void;
}
class ExpensiveService implements IExpensiveService {
performCalculation(): number {
return Math.random() * 1000;
}
}
class AdvancedService {}
class BasicService {}
class VerboseLogger implements ILogger {
log(message: string): void {
console.log(`[VERBOSE] ${message}`);
}
}
class ProductionLogger implements ILogger {
log(message: string): void {
console.log(message);
}
}
class DatabaseConnection {}
class UserRepository {}
class ProductRepository {}
class HttpClient {
timeout: number = 5000;
constructor(private baseUrl: string) {}
}
class ApiService {}
class DatabasePool implements IDatabasePool {
constructor(private connectionString: string, private maxConnections: number) {}
async getConnection(): Promise<any> {
return {}; // Mock connection
}
}Integration with DOM elements for providing dependency injection within component hierarchies.
/**
* A container associated with DOM nodes
*/
interface DOMContainer extends Container {
/** The DOM node this container is associated with */
readonly owner: Node;
/**
* Gets the closest DOMContainer by walking up the DOM tree
* @param node - The starting node
*/
getClosest(node: Node): DOMContainer | null;
}
/**
* Service locator interface for dependency resolution
*/
interface ServiceLocator {
/**
* Gets an instance of a dependency
* @param key - The key identifying the dependency
*/
get<K extends Key>(key: K): Resolved<K>;
/**
* Gets an instance of a dependency asynchronously
* @param key - The key identifying the dependency
*/
getAsync<K extends Key>(key: K): Promise<Resolved<K>>;
/**
* Gets all instances of a dependency
* @param key - The key identifying the dependency
* @param searchAncestors - Whether to search ancestor containers
*/
getAll<K extends Key>(key: K, searchAncestors?: boolean): readonly Resolved<K>[];
/**
* Checks if a dependency is registered
* @param key - The key to check
* @param searchAncestors - Whether to search ancestor containers
*/
has<K extends Key>(key: K, searchAncestors?: boolean): boolean;
}
/**
* Context keys for DI integration
*/
const Container: Context<Container>;
const DOMContainer: Context<DOMContainer>;
const ServiceLocator: Context<ServiceLocator>;Usage Examples:
import {
DI,
DOMContainer as DOMContainerContext,
Container as ContainerContext,
FASTElement,
customElement,
html
} from "@microsoft/fast-element";
// Root application container setup
class Application {
private rootContainer: Container;
constructor() {
this.rootContainer = DI.createContainer();
this.setupRootServices();
}
private setupRootServices() {
this.rootContainer.register(
Registration.singleton(IAppConfig, AppConfig),
Registration.singleton(IThemeService, ThemeService),
Registration.singleton(IAuthService, AuthService),
Registration.instance('apiBaseUrl', 'https://api.example.com')
);
}
bootstrap(rootElement: HTMLElement) {
// Associate root container with DOM
const domContainer = DI.getOrCreateDOMContainer(rootElement);
// Copy root registrations to DOM container
domContainer.register(
...this.rootContainer.getAll(Registration) // Copy all registrations
);
// Render root component
const appComponent = document.createElement('app-root');
rootElement.appendChild(appComponent);
}
}
// Application-level component
@customElement("app-root")
export class AppRoot extends FASTElement {
private themeService?: IThemeService;
private authService?: IAuthService;
connectedCallback() {
super.connectedCallback();
// Get services from DOM container hierarchy
const domContainer = DI.getOrCreateDOMContainer(this);
this.themeService = domContainer.get(IThemeService);
this.authService = domContainer.get(IAuthService);
this.initializeApp();
}
private async initializeApp() {
// Initialize theme
await this.themeService?.initialize();
// Check authentication
const isAuthenticated = await this.authService?.checkAuth();
if (isAuthenticated) {
this.renderAuthenticatedApp();
} else {
this.renderLoginApp();
}
}
private renderAuthenticatedApp() {
// Create child container for authenticated features
const domContainer = DI.getOrCreateDOMContainer(this);
const childContainer = domContainer.createChild();
// Register authenticated services
childContainer.register(
Registration.singleton(IUserService, UserService),
Registration.singleton(INotificationService, NotificationService)
);
// Associate child container with section
const mainSection = this.shadowRoot?.querySelector('#main-content') as HTMLElement;
if (mainSection) {
const sectionContainer = DI.getOrCreateDOMContainer(mainSection);
// Copy child registrations
sectionContainer.register(
Registration.alias(IUserService, IUserService),
Registration.alias(INotificationService, INotificationService)
);
}
}
private renderLoginApp() {
// Different services for login flow
const domContainer = DI.getOrCreateDOMContainer(this);
const loginContainer = domContainer.createChild();
loginContainer.register(
Registration.transient(ILoginService, LoginService),
Registration.singleton(IValidationService, ValidationService)
);
}
static template = html<AppRoot>`
<div class="app-root">
<header id="app-header">
<app-header></app-header>
</header>
<main id="main-content">
<router-outlet></router-outlet>
</main>
<footer id="app-footer">
<app-footer></app-footer>
</footer>
</div>
`;
}
// Feature component that uses parent container services
@customElement("user-profile")
export class UserProfile extends FASTElement {
private userService?: IUserService;
private notificationService?: INotificationService;
connectedCallback() {
super.connectedCallback();
// Automatically finds services up the DOM tree
const domContainer = DI.getOrCreateDOMContainer(this);
this.userService = domContainer.get(IUserService);
this.notificationService = domContainer.get(INotificationService);
}
private async saveProfile(profileData: any) {
try {
await this.userService?.updateProfile(profileData);
this.notificationService?.showSuccess('Profile updated successfully');
} catch (error) {
this.notificationService?.showError('Failed to update profile');
}
}
static template = html<UserProfile>`
<div class="user-profile">
<h2>User Profile</h2>
<!-- Profile form content -->
</div>
`;
}
// Context-aware service that can access its container
class ContextAwareService {
constructor(@inject(ContainerContext) private container: Container) {}
getRelatedService<T>(key: Key): T {
// Can resolve other services from the same container
return this.container.get(key);
}
createChildScope(): Container {
// Can create child scopes for temporary operations
return this.container.createChild();
}
}
// Service that needs DOM context
class DOMContextService {
constructor(@inject(DOMContainerContext) private domContainer: DOMContainer) {}
findNearestService<T>(key: Key): T | null {
// Walk up DOM tree looking for service
let current = this.domContainer.owner.parentElement;
while (current) {
const container = DI.getOrCreateDOMContainer(current);
if (container.has(key)) {
return container.get(key);
}
current = current.parentElement;
}
return null;
}
broadcastToChildren(message: any) {
// Find all child DOM containers and send message
const walker = document.createTreeWalker(
this.domContainer.owner,
NodeFilter.SHOW_ELEMENT
);
let node = walker.nextNode();
while (node) {
const childContainer = DI.getOrCreateDOMContainer(node as Element);
if (childContainer.has(IMessageHandler)) {
const handler = childContainer.get(IMessageHandler);
handler.handleMessage(message);
}
node = walker.nextNode();
}
}
}
// Module-based registration
class FeatureModule {
static register(container: Container) {
container.register(
Registration.singleton(IFeatureService, FeatureService),
Registration.transient(IFeatureRepository, FeatureRepository),
Registration.instance('featureConfig', {
enabled: true,
maxItems: 100
})
);
}
}
// Usage in application
const app = new Application();
app.bootstrap(document.getElementById('app')!);
// Mock interfaces for examples
interface IAppConfig {
apiUrl: string;
version: string;
}
interface IThemeService {
initialize(): Promise<void>;
setTheme(theme: string): void;
}
interface IAuthService {
checkAuth(): Promise<boolean>;
login(credentials: any): Promise<void>;
}
interface IUserService {
updateProfile(data: any): Promise<void>;
}
interface INotificationService {
showSuccess(message: string): void;
showError(message: string): void;
}
interface ILoginService {
validateCredentials(credentials: any): Promise<boolean>;
}
interface IValidationService {
validateForm(data: any): ValidationResult;
}
interface IMessageHandler {
handleMessage(message: any): void;
}
interface IFeatureService {}
interface IFeatureRepository {}
interface ValidationResult {
isValid: boolean;
errors: string[];
}
class AppConfig implements IAppConfig {
apiUrl = 'https://api.example.com';
version = '1.0.0';
}
class ThemeService implements IThemeService {
async initialize(): Promise<void> {}
setTheme(theme: string): void {}
}
class AuthService implements IAuthService {
async checkAuth(): Promise<boolean> { return false; }
async login(credentials: any): Promise<void> {}
}
class UserService implements IUserService {
async updateProfile(data: any): Promise<void> {}
}
class NotificationService implements INotificationService {
showSuccess(message: string): void {}
showError(message: string): void {}
}
class LoginService implements ILoginService {
async validateCredentials(credentials: any): Promise<boolean> { return true; }
}
class ValidationService implements IValidationService {
validateForm(data: any): ValidationResult {
return { isValid: true, errors: [] };
}
}
class FeatureService implements IFeatureService {}
class FeatureRepository implements IFeatureRepository {}/**
* Dependency injection key type
*/
type Key = PropertyKey | Constructable | object;
/**
* Resolved type helper
*/
type Resolved<K> = K extends Constructable<infer T> ? T : K;
/**
* Injectable class type
*/
type Injectable<T = {}> = Constructable<T> & { register?(container: Container): void };
/**
* Self-registering type
*/
type RegisterSelf<T extends Constructable> = T & Registration<InstanceType<T>>;
/**
* Parent container locator function
*/
type ParentLocator = (container: Container) => Container | null;
/**
* Async registration locator function
*/
type AsyncRegistrationLocator = (container: Container) => Promise<Registration[]>;