0
# Dependency Injection System
1
2
Theia Core provides comprehensive dependency injection infrastructure built on InversifyJS, featuring Symbol-based service tokens, contribution patterns, and container management for building extensible applications.
3
4
## Capabilities
5
6
### Injectable Decorator
7
8
Marks a class as injectable for dependency injection container binding.
9
10
```typescript { .api }
11
/**
12
* Marks a class as available for injection
13
* @param target - The class to mark as injectable
14
* @returns The injectable class
15
*/
16
function injectable<T>(target: interfaces.Newable<T>): interfaces.Newable<T>;
17
```
18
19
**Usage Example:**
20
21
```typescript
22
import { injectable } from "@theia/core";
23
24
@injectable()
25
export class MyService {
26
doSomething(): void {
27
console.log("Service method called");
28
}
29
}
30
```
31
32
### Inject Decorator
33
34
Injects dependencies into constructor parameters or properties.
35
36
```typescript { .api }
37
/**
38
* Injects a service into a constructor parameter
39
* @param serviceIdentifier - Symbol or string identifying the service
40
* @returns Parameter decorator
41
*/
42
function inject(serviceIdentifier: interfaces.ServiceIdentifier): ParameterDecorator;
43
```
44
45
**Usage Examples:**
46
47
```typescript
48
import { injectable, inject } from "@theia/core";
49
import { ILogger } from "@theia/core";
50
51
@injectable()
52
export class MyComponent {
53
constructor(
54
@inject(ILogger) private readonly logger: ILogger
55
) {}
56
57
performAction(): void {
58
this.logger.info("Action performed");
59
}
60
}
61
```
62
63
### Contribution Provider
64
65
Collects and provides access to all bound contributions of a specific type.
66
67
```typescript { .api }
68
/**
69
* Provider for collecting contributions of a specific type
70
*/
71
interface ContributionProvider<T> {
72
/**
73
* Get all contributions of type T
74
* @returns Array of all bound contributions
75
*/
76
getContributions(): T[];
77
78
/**
79
* Get all contributions of type T that satisfy the filter
80
* @param filter - Optional filter function
81
* @returns Filtered array of contributions
82
*/
83
getContributions(filter?: (contrib: T) => boolean): T[];
84
}
85
86
/**
87
* Symbol for binding ContributionProvider
88
*/
89
const ContributionProvider: symbol;
90
```
91
92
**Usage Example:**
93
94
```typescript
95
import { injectable, inject } from "@theia/core";
96
import { ContributionProvider, CommandContribution } from "@theia/core";
97
98
@injectable()
99
export class CommandManager {
100
constructor(
101
@inject(ContributionProvider) @named(CommandContribution)
102
private readonly contributions: ContributionProvider<CommandContribution>
103
) {}
104
105
initializeCommands(): void {
106
for (const contribution of this.contributions.getContributions()) {
107
contribution.registerCommands(/* registry */);
108
}
109
}
110
}
111
```
112
113
### Binding Functions
114
115
Utility functions for binding contributions and providers to the container.
116
117
```typescript { .api }
118
/**
119
* Binds a contribution provider to the container
120
* @param bind - Container bind function
121
* @param id - Service identifier for the contribution type
122
*/
123
function bindContributionProvider(
124
bind: interfaces.Bind,
125
id: interfaces.ServiceIdentifier
126
): void;
127
128
/**
129
* Binds a single contribution to the container
130
* @param bind - Container bind function
131
* @param id - Service identifier for the contribution type
132
* @param contribution - The contribution class to bind
133
*/
134
function bindContribution<T>(
135
bind: interfaces.Bind,
136
id: interfaces.ServiceIdentifier<T>,
137
contribution: interfaces.Newable<T>
138
): void;
139
```
140
141
**Usage Example:**
142
143
```typescript
144
import { ContainerModule } from "inversify";
145
import { bindContributionProvider, CommandContribution } from "@theia/core";
146
147
export default new ContainerModule(bind => {
148
// Bind the contribution provider
149
bindContributionProvider(bind, CommandContribution);
150
151
// Bind individual contributions
152
bind(CommandContribution).to(MyCommandContribution).inSingletonScope();
153
bind(CommandContribution).to(AnotherCommandContribution).inSingletonScope();
154
});
155
```
156
157
### Named Binding
158
159
Decorator for named service bindings when multiple implementations exist.
160
161
```typescript { .api }
162
/**
163
* Named binding decorator for distinguishing between multiple implementations
164
* @param name - The name to identify this binding
165
* @returns Parameter decorator
166
*/
167
function named(name: string | number | symbol): ParameterDecorator;
168
```
169
170
**Usage Example:**
171
172
```typescript
173
import { injectable, inject, named } from "@theia/core";
174
175
interface StorageService {
176
get(key: string): string | undefined;
177
set(key: string, value: string): void;
178
}
179
180
@injectable()
181
export class ConfigurationManager {
182
constructor(
183
@inject('StorageService') @named('user')
184
private readonly userStorage: StorageService,
185
186
@inject('StorageService') @named('workspace')
187
private readonly workspaceStorage: StorageService
188
) {}
189
}
190
```
191
192
### Container Management
193
194
Utilities for working with InversifyJS containers in Theia applications.
195
196
```typescript { .api }
197
/**
198
* Application container type
199
*/
200
type ApplicationContainer = interfaces.Container;
201
202
/**
203
* Bindable type for flexible binding
204
*/
205
type Bindable = interfaces.Bind | interfaces.Container;
206
```
207
208
**Usage Example:**
209
210
```typescript
211
import { Container, ContainerModule } from "inversify";
212
import { bindContributionProvider } from "@theia/core";
213
214
// Create application container
215
const container = new Container();
216
217
// Load extension modules
218
const extensionModule = new ContainerModule(bind => {
219
bindContributionProvider(bind, CommandContribution);
220
bind(MyService).toSelf().inSingletonScope();
221
});
222
223
container.load(extensionModule);
224
225
// Get services
226
const myService = container.get(MyService);
227
```
228
229
### Optional Injection
230
231
Handle optional dependencies that may not be bound.
232
233
```typescript { .api }
234
/**
235
* Optional injection decorator
236
* @param serviceIdentifier - Service identifier
237
* @returns Parameter decorator that allows undefined injection
238
*/
239
function optional(): ParameterDecorator;
240
```
241
242
**Usage Example:**
243
244
```typescript
245
import { injectable, inject, optional } from "@theia/core";
246
247
@injectable()
248
export class OptionalServiceConsumer {
249
constructor(
250
@inject('RequiredService') private readonly required: RequiredService,
251
@inject('OptionalService') @optional()
252
private readonly optional?: OptionalService
253
) {}
254
255
performAction(): void {
256
// Always available
257
this.required.doSomething();
258
259
// Check if optional service is available
260
if (this.optional) {
261
this.optional.doOptionalThing();
262
}
263
}
264
}
265
```
266
267
## Service Binding Patterns
268
269
### Singleton Scope
270
271
Most Theia services should be bound in singleton scope:
272
273
```typescript
274
bind(MyService).toSelf().inSingletonScope();
275
```
276
277
### Contribution Pattern
278
279
Extensions contribute implementations through the contribution pattern:
280
281
```typescript
282
// 1. Define contribution interface
283
interface MyContribution {
284
configure(): void;
285
}
286
287
// 2. Bind contribution provider
288
bindContributionProvider(bind, MyContribution);
289
290
// 3. Extensions bind their implementations
291
bind(MyContribution).to(MyImplementation).inSingletonScope();
292
293
// 4. Core service consumes all contributions
294
@injectable()
295
export class MyManager {
296
constructor(
297
@inject(ContributionProvider) @named(MyContribution)
298
private readonly contributions: ContributionProvider<MyContribution>
299
) {}
300
301
initialize(): void {
302
for (const contrib of this.contributions.getContributions()) {
303
contrib.configure();
304
}
305
}
306
}
307
```
308
309
### Service Factories
310
311
Create services with runtime parameters:
312
313
```typescript
314
bind('ServiceFactory').toFactory(context =>
315
(config: ServiceConfig) => {
316
const service = context.container.get(ServiceImpl);
317
service.configure(config);
318
return service;
319
}
320
);
321
```
322
323
## Types
324
325
```typescript { .api }
326
// InversifyJS types re-exported from Theia
327
type ServiceIdentifier<T = any> = interfaces.ServiceIdentifier<T>;
328
type Bind = interfaces.Bind;
329
type Container = interfaces.Container;
330
type ContainerModule = interfaces.ContainerModule;
331
332
interface Newable<T> {
333
new (...args: any[]): T;
334
}
335
336
interface Abstract<T> {
337
prototype: T;
338
}
339
```