0
# Dependency Injection
1
2
Full-featured dependency injection container with decorators, service registration, and hierarchical resolution for building modular and testable applications.
3
4
## Capabilities
5
6
### DI Container
7
8
Core container for managing dependency registration, resolution, and lifetime management with hierarchical scoping support.
9
10
```typescript { .api }
11
/**
12
* Central DI container interface
13
*/
14
interface Container extends ServiceLocator {
15
/**
16
* Gets an instance of a dependency
17
* @param key - The key that identifies the dependency
18
*/
19
get<K extends Key>(key: K): Resolved<K>;
20
21
/**
22
* Gets an instance of a dependency asynchronously
23
* @param key - The key that identifies the dependency
24
*/
25
getAsync<K extends Key>(key: K): Promise<Resolved<K>>;
26
27
/**
28
* Gets all instances of a dependency
29
* @param key - The key that identifies the dependency
30
* @param searchAncestors - Whether to search ancestor containers
31
*/
32
getAll<K extends Key>(key: K, searchAncestors?: boolean): readonly Resolved<K>[];
33
34
/**
35
* Registers dependencies with the container
36
* @param registrations - The registrations to add
37
*/
38
register(...registrations: Registration[]): Container;
39
40
/**
41
* Registers a resolver for a specific key
42
* @param key - The key to register the resolver for
43
* @param resolver - The resolver to register
44
*/
45
registerResolver<K extends Key>(key: K, resolver: Resolver<K>): Resolver<K>;
46
47
/**
48
* Creates a child container
49
* @param config - Configuration for the child container
50
*/
51
createChild(config?: ContainerConfiguration): Container;
52
53
/**
54
* Disposes of the container and its resources
55
*/
56
dispose(): void;
57
}
58
59
/**
60
* Configuration options for containers
61
*/
62
interface ContainerConfiguration {
63
/** Parent container locator */
64
parentLocator?: ParentLocator;
65
66
/** Default resolver to use when no registration is found */
67
defaultResolver?: (key: Key, handler: Container) => Resolver | null;
68
69
/** Whether to inherit registrations from parent */
70
inheritParentResources?: boolean;
71
}
72
73
/**
74
* Main DI utilities object
75
*/
76
const DI: {
77
/**
78
* Creates a new DI container
79
* @param config - Container configuration
80
*/
81
createContainer(config?: ContainerConfiguration): Container;
82
83
/**
84
* Gets or creates a DOM container for an element
85
* @param node - The DOM node to associate with the container
86
*/
87
getOrCreateDOMContainer(node?: Node): DOMContainer;
88
89
/**
90
* Gets the design-time type associated with a key
91
* @param key - The key to get the type for
92
*/
93
getDesignTimeType<K extends Key>(key: K): K | null;
94
95
/**
96
* Gets dependencies for a type
97
* @param Type - The type to get dependencies for
98
*/
99
getDependencies(Type: Constructable): Key[];
100
101
/**
102
* Defines dependencies for a type
103
* @param Type - The type to define dependencies for
104
* @param dependencies - The dependencies
105
*/
106
defineDependencies(Type: Constructable, dependencies: Key[]): void;
107
108
/**
109
* Gets the factory for a type
110
* @param Type - The type to get the factory for
111
*/
112
getFactory<T extends Constructable>(Type: T): Factory<T>;
113
};
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import { DI, Container, inject, singleton, transient } from "@microsoft/fast-element/di.js";
120
121
// Service interfaces and implementations
122
interface ILogger {
123
log(message: string): void;
124
error(message: string): void;
125
}
126
127
interface IApiClient {
128
get(url: string): Promise<any>;
129
post(url: string, data: any): Promise<any>;
130
}
131
132
interface IUserService {
133
getCurrentUser(): Promise<User>;
134
updateUser(user: User): Promise<void>;
135
}
136
137
// Service implementations
138
@singleton()
139
class ConsoleLogger implements ILogger {
140
log(message: string): void {
141
console.log(`[LOG] ${new Date().toISOString()}: ${message}`);
142
}
143
144
error(message: string): void {
145
console.error(`[ERROR] ${new Date().toISOString()}: ${message}`);
146
}
147
}
148
149
@singleton()
150
class HttpApiClient implements IApiClient {
151
constructor(@inject('baseUrl') private baseUrl: string) {}
152
153
async get(url: string): Promise<any> {
154
const response = await fetch(`${this.baseUrl}${url}`);
155
return response.json();
156
}
157
158
async post(url: string, data: any): Promise<any> {
159
const response = await fetch(`${this.baseUrl}${url}`, {
160
method: 'POST',
161
headers: { 'Content-Type': 'application/json' },
162
body: JSON.stringify(data)
163
});
164
return response.json();
165
}
166
}
167
168
@transient()
169
class UserService implements IUserService {
170
constructor(
171
@inject(IApiClient) private apiClient: IApiClient,
172
@inject(ILogger) private logger: ILogger
173
) {}
174
175
async getCurrentUser(): Promise<User> {
176
this.logger.log('Fetching current user');
177
try {
178
return await this.apiClient.get('/user/current');
179
} catch (error) {
180
this.logger.error(`Failed to fetch user: ${error}`);
181
throw error;
182
}
183
}
184
185
async updateUser(user: User): Promise<void> {
186
this.logger.log(`Updating user ${user.id}`);
187
try {
188
await this.apiClient.post('/user/update', user);
189
} catch (error) {
190
this.logger.error(`Failed to update user: ${error}`);
191
throw error;
192
}
193
}
194
}
195
196
// Container setup and usage
197
const container = DI.createContainer();
198
199
// Register dependencies
200
container.register(
201
// Register interfaces to implementations
202
Registration.singleton(ILogger, ConsoleLogger),
203
Registration.singleton(IApiClient, HttpApiClient),
204
Registration.transient(IUserService, UserService),
205
206
// Register configuration values
207
Registration.instance('baseUrl', 'https://api.example.com'),
208
Registration.instance('appName', 'My Application')
209
);
210
211
// Resolve and use services
212
const userService = container.get(IUserService);
213
const logger = container.get(ILogger);
214
215
// Services are now ready to use
216
logger.log('Application starting');
217
218
// Example component using DI
219
@customElement("di-example")
220
export class DIExample extends FASTElement {
221
constructor(
222
@inject(IUserService) private userService: IUserService,
223
@inject(ILogger) private logger: ILogger
224
) {
225
super();
226
}
227
228
async connectedCallback() {
229
super.connectedCallback();
230
231
try {
232
const user = await this.userService.getCurrentUser();
233
this.logger.log(`User ${user.name} connected`);
234
} catch (error) {
235
this.logger.error('Failed to load user data');
236
}
237
}
238
239
static template = html`
240
<div>Component with injected dependencies</div>
241
`;
242
}
243
244
// Create child container with additional registrations
245
const childContainer = container.createChild();
246
childContainer.register(
247
Registration.instance('environment', 'development')
248
);
249
250
// Child container inherits parent registrations
251
const childLogger = childContainer.get(ILogger); // Same instance as parent
252
const environment = childContainer.get('environment'); // 'development'
253
254
interface User {
255
id: string;
256
name: string;
257
email: string;
258
}
259
```
260
261
### Injection Decorators
262
263
Decorators for marking dependencies to be injected and configuring injection behavior.
264
265
```typescript { .api }
266
/**
267
* Decorator for dependency injection
268
* @param keys - The keys to inject
269
*/
270
function inject(...keys: Key[]): ParameterDecorator | PropertyDecorator;
271
272
/**
273
* Decorator for singleton registration
274
* @param RegisterTarget - Optional registration target
275
*/
276
function singleton<T extends Constructable>(RegisterTarget?: T): RegisterSelf<T>;
277
278
/**
279
* Decorator for transient registration
280
* @param RegisterTarget - Optional registration target
281
*/
282
function transient<T extends Constructable>(RegisterTarget?: T): RegisterSelf<T>;
283
284
/**
285
* Creates a resolver that resolves all instances of a key
286
* @param key - The key to resolve all instances for
287
*/
288
function all<K extends Key>(key: K): Key;
289
290
/**
291
* Creates a lazy resolver that defers resolution
292
* @param key - The key to resolve lazily
293
*/
294
function lazy<K extends Key>(key: K): Key;
295
296
/**
297
* Creates an optional resolver that returns null if not found
298
* @param key - The key to resolve optionally
299
*/
300
function optional<K extends Key>(key: K): Key;
301
302
/**
303
* Marks a parameter to be ignored during injection
304
*/
305
function ignore<K extends Key>(key: K): Key;
306
307
/**
308
* Creates a resolver for new instances per scope
309
* @param key - The key to create new instances for
310
*/
311
function newInstanceForScope<K extends Key>(key: K): Key;
312
313
/**
314
* Creates a resolver for new instances of a type
315
* @param key - The key to create new instances for
316
*/
317
function newInstanceOf<K extends Key>(key: K): Key;
318
```
319
320
**Usage Examples:**
321
322
```typescript
323
import {
324
inject,
325
singleton,
326
transient,
327
all,
328
lazy,
329
optional,
330
ignore,
331
newInstanceForScope,
332
newInstanceOf,
333
DI
334
} from "@microsoft/fast-element";
335
336
// Service interfaces
337
interface IPlugin {
338
name: string;
339
initialize(): void;
340
}
341
342
interface INotificationService {
343
send(message: string): void;
344
}
345
346
interface IConfigService {
347
get(key: string): any;
348
}
349
350
// Plugin implementations
351
@transient()
352
class EmailPlugin implements IPlugin {
353
name = "Email Plugin";
354
355
initialize(): void {
356
console.log("Email plugin initialized");
357
}
358
}
359
360
@transient()
361
class PushPlugin implements IPlugin {
362
name = "Push Plugin";
363
364
initialize(): void {
365
console.log("Push plugin initialized");
366
}
367
}
368
369
// Services with different injection patterns
370
@singleton()
371
class NotificationService implements INotificationService {
372
constructor(
373
// Inject all registered plugins
374
@inject(all(IPlugin)) private plugins: IPlugin[],
375
376
// Optional dependency - won't fail if not registered
377
@inject(optional(IConfigService)) private config?: IConfigService,
378
379
// Lazy injection - resolved when first accessed
380
@inject(lazy(ILogger)) private getLogger?: () => ILogger
381
) {}
382
383
send(message: string): void {
384
// Use logger if available (lazy resolution)
385
const logger = this.getLogger?.();
386
logger?.log(`Sending notification: ${message}`);
387
388
// Initialize all plugins
389
this.plugins.forEach(plugin => {
390
plugin.initialize();
391
});
392
393
// Use config if available
394
const timeout = this.config?.get('notificationTimeout') || 5000;
395
console.log(`Notification sent with timeout: ${timeout}ms`);
396
}
397
}
398
399
// Service with ignored parameters
400
@singleton()
401
class DatabaseService {
402
constructor(
403
@inject('connectionString') private connectionString: string,
404
@ignore private debugInfo?: string, // Not injected, can be passed manually
405
@inject(optional(ILogger)) private logger?: ILogger
406
) {
407
this.logger?.log(`Database service created with connection: ${connectionString}`);
408
}
409
}
410
411
// Service with new instance resolvers
412
@singleton()
413
class TaskProcessor {
414
constructor(
415
// Each task gets a new worker instance
416
@inject(newInstanceOf(ITaskWorker)) private createWorker: () => ITaskWorker,
417
418
// New session per processing scope
419
@inject(newInstanceForScope(IProcessingSession)) private getSession: () => IProcessingSession,
420
421
@inject(ILogger) private logger: ILogger
422
) {}
423
424
async processTask(task: Task): Promise<void> {
425
const worker = this.createWorker();
426
const session = this.getSession();
427
428
this.logger.log(`Processing task ${task.id} with worker ${worker.id}`);
429
430
try {
431
await worker.execute(task);
432
session.markCompleted(task.id);
433
} catch (error) {
434
session.markFailed(task.id, error);
435
throw error;
436
}
437
}
438
}
439
440
// Component with complex injection
441
@customElement("complex-di-example")
442
export class ComplexDIExample extends FASTElement {
443
private notificationService: INotificationService;
444
private taskProcessor: TaskProcessor;
445
private allPlugins: IPlugin[];
446
447
constructor(
448
@inject(INotificationService) notificationService: INotificationService,
449
@inject(TaskProcessor) taskProcessor: TaskProcessor,
450
@inject(all(IPlugin)) allPlugins: IPlugin[]
451
) {
452
super();
453
this.notificationService = notificationService;
454
this.taskProcessor = taskProcessor;
455
this.allPlugins = allPlugins;
456
}
457
458
connectedCallback() {
459
super.connectedCallback();
460
461
// Use injected services
462
this.notificationService.send("Component connected");
463
464
console.log(`Available plugins: ${this.allPlugins.map(p => p.name).join(', ')}`);
465
}
466
467
async processExampleTask() {
468
const task: Task = {
469
id: 'example-task',
470
data: { message: 'Hello World' },
471
priority: 'normal'
472
};
473
474
try {
475
await this.taskProcessor.processTask(task);
476
this.notificationService.send("Task processed successfully");
477
} catch (error) {
478
this.notificationService.send("Task processing failed");
479
}
480
}
481
482
static template = html<ComplexDIExample>`
483
<div class="complex-di-demo">
484
<h2>Complex DI Example</h2>
485
<p>This component uses various injection patterns</p>
486
<button @click="${x => x.processExampleTask()}">
487
Process Task
488
</button>
489
</div>
490
`;
491
}
492
493
// Container setup with all registrations
494
const container = DI.createContainer();
495
496
container.register(
497
// Register all plugins
498
Registration.transient(IPlugin, EmailPlugin),
499
Registration.transient(IPlugin, PushPlugin),
500
501
// Register services
502
Registration.singleton(INotificationService, NotificationService),
503
Registration.singleton(TaskProcessor, TaskProcessor),
504
Registration.singleton(DatabaseService, DatabaseService),
505
506
// Register configuration
507
Registration.instance('connectionString', 'mongodb://localhost:27017'),
508
Registration.instance(IConfigService, {
509
get: (key: string) => {
510
const config = { notificationTimeout: 3000, maxRetries: 3 };
511
return config[key as keyof typeof config];
512
}
513
})
514
);
515
516
interface ITaskWorker {
517
id: string;
518
execute(task: Task): Promise<void>;
519
}
520
521
interface IProcessingSession {
522
markCompleted(taskId: string): void;
523
markFailed(taskId: string, error: any): void;
524
}
525
526
interface Task {
527
id: string;
528
data: any;
529
priority: 'low' | 'normal' | 'high';
530
}
531
```
532
533
### Service Registration
534
535
Registration utilities for configuring how services are resolved and their lifetimes.
536
537
```typescript { .api }
538
/**
539
* Registration utilities for DI container
540
*/
541
const Registration: {
542
/**
543
* Creates a transient registration (new instance each time)
544
* @param key - The key to register under
545
* @param value - The value or type to register
546
*/
547
transient<T>(key: Key, value: Constructable<T> | T): Registration<T>;
548
549
/**
550
* Creates a singleton registration (same instance each time)
551
* @param key - The key to register under
552
* @param value - The value or type to register
553
*/
554
singleton<T>(key: Key, value: Constructable<T> | T): Registration<T>;
555
556
/**
557
* Creates an instance registration (specific instance)
558
* @param key - The key to register under
559
* @param value - The instance to register
560
*/
561
instance<T>(key: Key, value: T): Registration<T>;
562
563
/**
564
* Creates a callback registration (custom resolution logic)
565
* @param key - The key to register under
566
* @param callback - The callback to resolve the dependency
567
*/
568
callback<T>(key: Key, callback: ResolveCallback<T>): Registration<T>;
569
570
/**
571
* Creates a cached callback registration
572
* @param key - The key to register under
573
* @param callback - The callback to resolve the dependency
574
*/
575
cachedCallback<T>(key: Key, callback: ResolveCallback<T>): Registration<T>;
576
577
/**
578
* Creates an aliased registration (redirects to another key)
579
* @param originalKey - The original key
580
* @param aliasKey - The alias key
581
*/
582
alias<T>(originalKey: Key, aliasKey: Key): Registration<T>;
583
584
/**
585
* Creates a defer registration (resolves when first requested)
586
* @param key - The key to register under
587
* @param callback - The callback to create the registration
588
*/
589
defer<T>(key: Key, callback: () => Registration<T>): Registration<T>;
590
};
591
592
/**
593
* Resolver interface for custom resolution logic
594
*/
595
interface Resolver<K = any> {
596
/** Marks this object as a resolver */
597
readonly $isResolver: true;
598
599
/**
600
* Resolves the dependency
601
* @param handler - The container handling the resolution
602
* @param requestor - The container that requested the resolution
603
*/
604
resolve(handler: Container, requestor: Container): Resolved<K>;
605
606
/**
607
* Resolves the dependency asynchronously
608
* @param handler - The container handling the resolution
609
* @param requestor - The container that requested the resolution
610
*/
611
resolveAsync(handler: Container, requestor: Container): Promise<Resolved<K>>;
612
613
/**
614
* Gets the factory for this resolver
615
* @param container - The container to get the factory from
616
*/
617
getFactory?(container: Container): Factory<K> | null;
618
}
619
```
620
621
**Usage Examples:**
622
623
```typescript
624
import { Registration, DI, Container, ResolveCallback } from "@microsoft/fast-element/di.js";
625
626
// Service interfaces
627
interface IDataService {
628
getData(): Promise<any>;
629
}
630
631
interface ICacheService {
632
get(key: string): any;
633
set(key: string, value: any): void;
634
}
635
636
interface IMetricsService {
637
recordEvent(event: string): void;
638
}
639
640
// Service implementations
641
class ApiDataService implements IDataService {
642
constructor(private apiUrl: string) {}
643
644
async getData(): Promise<any> {
645
const response = await fetch(`${this.apiUrl}/data`);
646
return response.json();
647
}
648
}
649
650
class MemoryCacheService implements ICacheService {
651
private cache = new Map<string, any>();
652
653
get(key: string): any {
654
return this.cache.get(key);
655
}
656
657
set(key: string, value: any): void {
658
this.cache.set(key, value);
659
}
660
}
661
662
class MetricsService implements IMetricsService {
663
recordEvent(event: string): void {
664
console.log(`Metrics: ${event} at ${new Date().toISOString()}`);
665
}
666
}
667
668
// Container setup with different registration types
669
const container = DI.createContainer();
670
671
// 1. Instance registration - specific instance
672
const cacheInstance = new MemoryCacheService();
673
container.register(
674
Registration.instance(ICacheService, cacheInstance)
675
);
676
677
// 2. Singleton registration - same instance each time
678
container.register(
679
Registration.singleton(IMetricsService, MetricsService)
680
);
681
682
// 3. Transient registration - new instance each time
683
container.register(
684
Registration.transient(IDataService, ApiDataService)
685
);
686
687
// 4. Callback registration - custom resolution logic
688
const dataServiceCallback: ResolveCallback<IDataService> = (handler, requestor, resolver) => {
689
const environment = handler.get('environment');
690
const apiUrl = environment === 'production'
691
? 'https://api.prod.example.com'
692
: 'https://api.dev.example.com';
693
694
return new ApiDataService(apiUrl);
695
};
696
697
container.register(
698
Registration.callback(IDataService, dataServiceCallback)
699
);
700
701
// 5. Cached callback registration - callback result cached
702
const expensiveServiceCallback: ResolveCallback<IExpensiveService> = (handler, requestor, resolver) => {
703
console.log('Creating expensive service (this should only happen once)');
704
return new ExpensiveService();
705
};
706
707
container.register(
708
Registration.cachedCallback(IExpensiveService, expensiveServiceCallback)
709
);
710
711
// 6. Alias registration - redirect one key to another
712
container.register(
713
Registration.alias('cache', ICacheService),
714
Registration.alias('metrics', IMetricsService)
715
);
716
717
// 7. Defer registration - create registration when needed
718
container.register(
719
Registration.defer('conditionalService', () => {
720
const shouldUseAdvanced = container.get('useAdvancedFeatures');
721
722
return shouldUseAdvanced
723
? Registration.singleton('conditionalService', AdvancedService)
724
: Registration.singleton('conditionalService', BasicService);
725
})
726
);
727
728
// Complex registration scenarios
729
class ServiceFactory {
730
static createDatabaseRegistrations(connectionString: string): Registration[] {
731
return [
732
Registration.instance('connectionString', connectionString),
733
734
Registration.callback('dbConnection', (handler, requestor, resolver) => {
735
const connStr = handler.get('connectionString');
736
return new DatabaseConnection(connStr);
737
}),
738
739
Registration.singleton('userRepository', UserRepository),
740
Registration.singleton('productRepository', ProductRepository)
741
];
742
}
743
744
static createHttpClientRegistrations(baseUrl: string, timeout: number): Registration[] {
745
return [
746
Registration.instance('httpConfig', { baseUrl, timeout }),
747
748
Registration.cachedCallback('httpClient', (handler, requestor, resolver) => {
749
const config = handler.get('httpConfig');
750
const client = new HttpClient(config.baseUrl);
751
client.timeout = config.timeout;
752
return client;
753
}),
754
755
Registration.transient('apiService', ApiService)
756
];
757
}
758
}
759
760
// Register groups of related services
761
container.register(
762
...ServiceFactory.createDatabaseRegistrations('mongodb://localhost:27017'),
763
...ServiceFactory.createHttpClientRegistrations('https://api.example.com', 5000)
764
);
765
766
// Environment-specific registrations
767
const environment = process.env.NODE_ENV || 'development';
768
769
if (environment === 'development') {
770
container.register(
771
Registration.singleton(ILogger, VerboseLogger),
772
Registration.instance('debugMode', true)
773
);
774
} else {
775
container.register(
776
Registration.singleton(ILogger, ProductionLogger),
777
Registration.instance('debugMode', false)
778
);
779
}
780
781
// Custom resolver example
782
class DatabasePoolResolver implements Resolver<IDatabasePool> {
783
readonly $isResolver = true;
784
private pool?: IDatabasePool;
785
786
resolve(handler: Container, requestor: Container): IDatabasePool {
787
if (!this.pool) {
788
const connectionString = handler.get('connectionString');
789
const maxConnections = handler.get('maxConnections') || 10;
790
791
this.pool = new DatabasePool(connectionString, maxConnections);
792
}
793
794
return this.pool;
795
}
796
797
async resolveAsync(handler: Container, requestor: Container): Promise<IDatabasePool> {
798
return this.resolve(handler, requestor);
799
}
800
}
801
802
// Register custom resolver
803
container.registerResolver(IDatabasePool, new DatabasePoolResolver());
804
805
// Usage examples
806
const cache = container.get(ICacheService); // Gets the specific instance
807
const metrics1 = container.get(IMetricsService); // Gets singleton instance
808
const metrics2 = container.get(IMetricsService); // Same instance as metrics1
809
810
const data1 = container.get(IDataService); // Gets new transient instance
811
const data2 = container.get(IDataService); // Gets different transient instance
812
813
const aliasedCache = container.get('cache'); // Same as ICacheService instance
814
const aliasedMetrics = container.get('metrics'); // Same as IMetricsService instance
815
816
// Mock interfaces for examples
817
interface IExpensiveService {
818
performCalculation(): number;
819
}
820
821
interface IDatabasePool {
822
getConnection(): Promise<any>;
823
}
824
825
interface ILogger {
826
log(message: string): void;
827
}
828
829
class ExpensiveService implements IExpensiveService {
830
performCalculation(): number {
831
return Math.random() * 1000;
832
}
833
}
834
835
class AdvancedService {}
836
class BasicService {}
837
class VerboseLogger implements ILogger {
838
log(message: string): void {
839
console.log(`[VERBOSE] ${message}`);
840
}
841
}
842
843
class ProductionLogger implements ILogger {
844
log(message: string): void {
845
console.log(message);
846
}
847
}
848
849
class DatabaseConnection {}
850
class UserRepository {}
851
class ProductRepository {}
852
class HttpClient {
853
timeout: number = 5000;
854
constructor(private baseUrl: string) {}
855
}
856
class ApiService {}
857
class DatabasePool implements IDatabasePool {
858
constructor(private connectionString: string, private maxConnections: number) {}
859
860
async getConnection(): Promise<any> {
861
return {}; // Mock connection
862
}
863
}
864
```
865
866
### DOM Container Integration
867
868
Integration with DOM elements for providing dependency injection within component hierarchies.
869
870
```typescript { .api }
871
/**
872
* A container associated with DOM nodes
873
*/
874
interface DOMContainer extends Container {
875
/** The DOM node this container is associated with */
876
readonly owner: Node;
877
878
/**
879
* Gets the closest DOMContainer by walking up the DOM tree
880
* @param node - The starting node
881
*/
882
getClosest(node: Node): DOMContainer | null;
883
}
884
885
/**
886
* Service locator interface for dependency resolution
887
*/
888
interface ServiceLocator {
889
/**
890
* Gets an instance of a dependency
891
* @param key - The key identifying the dependency
892
*/
893
get<K extends Key>(key: K): Resolved<K>;
894
895
/**
896
* Gets an instance of a dependency asynchronously
897
* @param key - The key identifying the dependency
898
*/
899
getAsync<K extends Key>(key: K): Promise<Resolved<K>>;
900
901
/**
902
* Gets all instances of a dependency
903
* @param key - The key identifying the dependency
904
* @param searchAncestors - Whether to search ancestor containers
905
*/
906
getAll<K extends Key>(key: K, searchAncestors?: boolean): readonly Resolved<K>[];
907
908
/**
909
* Checks if a dependency is registered
910
* @param key - The key to check
911
* @param searchAncestors - Whether to search ancestor containers
912
*/
913
has<K extends Key>(key: K, searchAncestors?: boolean): boolean;
914
}
915
916
/**
917
* Context keys for DI integration
918
*/
919
const Container: Context<Container>;
920
const DOMContainer: Context<DOMContainer>;
921
const ServiceLocator: Context<ServiceLocator>;
922
```
923
924
**Usage Examples:**
925
926
```typescript
927
import {
928
DI,
929
DOMContainer as DOMContainerContext,
930
Container as ContainerContext,
931
FASTElement,
932
customElement,
933
html
934
} from "@microsoft/fast-element";
935
936
// Root application container setup
937
class Application {
938
private rootContainer: Container;
939
940
constructor() {
941
this.rootContainer = DI.createContainer();
942
this.setupRootServices();
943
}
944
945
private setupRootServices() {
946
this.rootContainer.register(
947
Registration.singleton(IAppConfig, AppConfig),
948
Registration.singleton(IThemeService, ThemeService),
949
Registration.singleton(IAuthService, AuthService),
950
Registration.instance('apiBaseUrl', 'https://api.example.com')
951
);
952
}
953
954
bootstrap(rootElement: HTMLElement) {
955
// Associate root container with DOM
956
const domContainer = DI.getOrCreateDOMContainer(rootElement);
957
958
// Copy root registrations to DOM container
959
domContainer.register(
960
...this.rootContainer.getAll(Registration) // Copy all registrations
961
);
962
963
// Render root component
964
const appComponent = document.createElement('app-root');
965
rootElement.appendChild(appComponent);
966
}
967
}
968
969
// Application-level component
970
@customElement("app-root")
971
export class AppRoot extends FASTElement {
972
private themeService?: IThemeService;
973
private authService?: IAuthService;
974
975
connectedCallback() {
976
super.connectedCallback();
977
978
// Get services from DOM container hierarchy
979
const domContainer = DI.getOrCreateDOMContainer(this);
980
this.themeService = domContainer.get(IThemeService);
981
this.authService = domContainer.get(IAuthService);
982
983
this.initializeApp();
984
}
985
986
private async initializeApp() {
987
// Initialize theme
988
await this.themeService?.initialize();
989
990
// Check authentication
991
const isAuthenticated = await this.authService?.checkAuth();
992
993
if (isAuthenticated) {
994
this.renderAuthenticatedApp();
995
} else {
996
this.renderLoginApp();
997
}
998
}
999
1000
private renderAuthenticatedApp() {
1001
// Create child container for authenticated features
1002
const domContainer = DI.getOrCreateDOMContainer(this);
1003
const childContainer = domContainer.createChild();
1004
1005
// Register authenticated services
1006
childContainer.register(
1007
Registration.singleton(IUserService, UserService),
1008
Registration.singleton(INotificationService, NotificationService)
1009
);
1010
1011
// Associate child container with section
1012
const mainSection = this.shadowRoot?.querySelector('#main-content') as HTMLElement;
1013
if (mainSection) {
1014
const sectionContainer = DI.getOrCreateDOMContainer(mainSection);
1015
// Copy child registrations
1016
sectionContainer.register(
1017
Registration.alias(IUserService, IUserService),
1018
Registration.alias(INotificationService, INotificationService)
1019
);
1020
}
1021
}
1022
1023
private renderLoginApp() {
1024
// Different services for login flow
1025
const domContainer = DI.getOrCreateDOMContainer(this);
1026
const loginContainer = domContainer.createChild();
1027
1028
loginContainer.register(
1029
Registration.transient(ILoginService, LoginService),
1030
Registration.singleton(IValidationService, ValidationService)
1031
);
1032
}
1033
1034
static template = html<AppRoot>`
1035
<div class="app-root">
1036
<header id="app-header">
1037
<app-header></app-header>
1038
</header>
1039
1040
<main id="main-content">
1041
<router-outlet></router-outlet>
1042
</main>
1043
1044
<footer id="app-footer">
1045
<app-footer></app-footer>
1046
</footer>
1047
</div>
1048
`;
1049
}
1050
1051
// Feature component that uses parent container services
1052
@customElement("user-profile")
1053
export class UserProfile extends FASTElement {
1054
private userService?: IUserService;
1055
private notificationService?: INotificationService;
1056
1057
connectedCallback() {
1058
super.connectedCallback();
1059
1060
// Automatically finds services up the DOM tree
1061
const domContainer = DI.getOrCreateDOMContainer(this);
1062
this.userService = domContainer.get(IUserService);
1063
this.notificationService = domContainer.get(INotificationService);
1064
}
1065
1066
private async saveProfile(profileData: any) {
1067
try {
1068
await this.userService?.updateProfile(profileData);
1069
this.notificationService?.showSuccess('Profile updated successfully');
1070
} catch (error) {
1071
this.notificationService?.showError('Failed to update profile');
1072
}
1073
}
1074
1075
static template = html<UserProfile>`
1076
<div class="user-profile">
1077
<h2>User Profile</h2>
1078
<!-- Profile form content -->
1079
</div>
1080
`;
1081
}
1082
1083
// Context-aware service that can access its container
1084
class ContextAwareService {
1085
constructor(@inject(ContainerContext) private container: Container) {}
1086
1087
getRelatedService<T>(key: Key): T {
1088
// Can resolve other services from the same container
1089
return this.container.get(key);
1090
}
1091
1092
createChildScope(): Container {
1093
// Can create child scopes for temporary operations
1094
return this.container.createChild();
1095
}
1096
}
1097
1098
// Service that needs DOM context
1099
class DOMContextService {
1100
constructor(@inject(DOMContainerContext) private domContainer: DOMContainer) {}
1101
1102
findNearestService<T>(key: Key): T | null {
1103
// Walk up DOM tree looking for service
1104
let current = this.domContainer.owner.parentElement;
1105
1106
while (current) {
1107
const container = DI.getOrCreateDOMContainer(current);
1108
if (container.has(key)) {
1109
return container.get(key);
1110
}
1111
current = current.parentElement;
1112
}
1113
1114
return null;
1115
}
1116
1117
broadcastToChildren(message: any) {
1118
// Find all child DOM containers and send message
1119
const walker = document.createTreeWalker(
1120
this.domContainer.owner,
1121
NodeFilter.SHOW_ELEMENT
1122
);
1123
1124
let node = walker.nextNode();
1125
while (node) {
1126
const childContainer = DI.getOrCreateDOMContainer(node as Element);
1127
if (childContainer.has(IMessageHandler)) {
1128
const handler = childContainer.get(IMessageHandler);
1129
handler.handleMessage(message);
1130
}
1131
node = walker.nextNode();
1132
}
1133
}
1134
}
1135
1136
// Module-based registration
1137
class FeatureModule {
1138
static register(container: Container) {
1139
container.register(
1140
Registration.singleton(IFeatureService, FeatureService),
1141
Registration.transient(IFeatureRepository, FeatureRepository),
1142
Registration.instance('featureConfig', {
1143
enabled: true,
1144
maxItems: 100
1145
})
1146
);
1147
}
1148
}
1149
1150
// Usage in application
1151
const app = new Application();
1152
app.bootstrap(document.getElementById('app')!);
1153
1154
// Mock interfaces for examples
1155
interface IAppConfig {
1156
apiUrl: string;
1157
version: string;
1158
}
1159
1160
interface IThemeService {
1161
initialize(): Promise<void>;
1162
setTheme(theme: string): void;
1163
}
1164
1165
interface IAuthService {
1166
checkAuth(): Promise<boolean>;
1167
login(credentials: any): Promise<void>;
1168
}
1169
1170
interface IUserService {
1171
updateProfile(data: any): Promise<void>;
1172
}
1173
1174
interface INotificationService {
1175
showSuccess(message: string): void;
1176
showError(message: string): void;
1177
}
1178
1179
interface ILoginService {
1180
validateCredentials(credentials: any): Promise<boolean>;
1181
}
1182
1183
interface IValidationService {
1184
validateForm(data: any): ValidationResult;
1185
}
1186
1187
interface IMessageHandler {
1188
handleMessage(message: any): void;
1189
}
1190
1191
interface IFeatureService {}
1192
interface IFeatureRepository {}
1193
1194
interface ValidationResult {
1195
isValid: boolean;
1196
errors: string[];
1197
}
1198
1199
class AppConfig implements IAppConfig {
1200
apiUrl = 'https://api.example.com';
1201
version = '1.0.0';
1202
}
1203
1204
class ThemeService implements IThemeService {
1205
async initialize(): Promise<void> {}
1206
setTheme(theme: string): void {}
1207
}
1208
1209
class AuthService implements IAuthService {
1210
async checkAuth(): Promise<boolean> { return false; }
1211
async login(credentials: any): Promise<void> {}
1212
}
1213
1214
class UserService implements IUserService {
1215
async updateProfile(data: any): Promise<void> {}
1216
}
1217
1218
class NotificationService implements INotificationService {
1219
showSuccess(message: string): void {}
1220
showError(message: string): void {}
1221
}
1222
1223
class LoginService implements ILoginService {
1224
async validateCredentials(credentials: any): Promise<boolean> { return true; }
1225
}
1226
1227
class ValidationService implements IValidationService {
1228
validateForm(data: any): ValidationResult {
1229
return { isValid: true, errors: [] };
1230
}
1231
}
1232
1233
class FeatureService implements IFeatureService {}
1234
class FeatureRepository implements IFeatureRepository {}
1235
```
1236
1237
## Types
1238
1239
```typescript { .api }
1240
/**
1241
* Dependency injection key type
1242
*/
1243
type Key = PropertyKey | Constructable | object;
1244
1245
/**
1246
* Resolved type helper
1247
*/
1248
type Resolved<K> = K extends Constructable<infer T> ? T : K;
1249
1250
/**
1251
* Injectable class type
1252
*/
1253
type Injectable<T = {}> = Constructable<T> & { register?(container: Container): void };
1254
1255
/**
1256
* Self-registering type
1257
*/
1258
type RegisterSelf<T extends Constructable> = T & Registration<InstanceType<T>>;
1259
1260
/**
1261
* Parent container locator function
1262
*/
1263
type ParentLocator = (container: Container) => Container | null;
1264
1265
/**
1266
* Async registration locator function
1267
*/
1268
type AsyncRegistrationLocator = (container: Container) => Promise<Registration[]>;
1269
```