0
# Decorators and Annotations
1
2
InversifyJS provides a comprehensive set of TypeScript decorators for marking classes as injectable and specifying dependency injection requirements. These decorators work with TypeScript's metadata reflection system to enable automatic dependency resolution.
3
4
## Core Decorators
5
6
### @injectable
7
8
Marks a class as available for dependency injection. This decorator is required on all classes that will be injected or serve as injection targets.
9
10
```typescript { .api }
11
function injectable<T = {}>(target: Newable<T>): void;
12
```
13
14
```typescript
15
@injectable()
16
class Warrior {
17
fight() {
18
return "Fighting!";
19
}
20
}
21
22
@injectable()
23
class Ninja extends Warrior {
24
private weapon: IWeapon;
25
26
constructor(@inject("Weapon") weapon: IWeapon) {
27
super();
28
this.weapon = weapon;
29
}
30
}
31
```
32
33
### @inject
34
35
Specifies which service should be injected into a constructor parameter or class property.
36
37
```typescript { .api }
38
function inject(serviceIdentifier: ServiceIdentifier): ParameterDecorator | PropertyDecorator;
39
```
40
41
#### Constructor Injection
42
43
```typescript
44
@injectable()
45
class Ninja {
46
constructor(
47
@inject("Weapon") private weapon: IWeapon,
48
@inject("Armor") private armor: IArmor
49
) {}
50
}
51
```
52
53
#### Property Injection
54
55
```typescript
56
@injectable()
57
class Ninja {
58
@inject("Weapon")
59
private weapon!: IWeapon;
60
61
@inject("Armor")
62
public armor!: IArmor;
63
}
64
```
65
66
### @multiInject
67
68
Injects an array of all services registered for a service identifier.
69
70
```typescript { .api }
71
function multiInject(serviceIdentifier: ServiceIdentifier): ParameterDecorator | PropertyDecorator;
72
```
73
74
```typescript
75
@injectable()
76
class Ninja {
77
constructor(
78
@multiInject("Weapon") private weapons: IWeapon[]
79
) {}
80
}
81
82
// Usage with property injection
83
@injectable()
84
class Warrior {
85
@multiInject("Skill")
86
private skills!: ISkill[];
87
}
88
```
89
90
## Conditional Decorators
91
92
### @named
93
94
Associates a name with an injection to enable conditional binding resolution.
95
96
```typescript { .api }
97
function named(name: string): ParameterDecorator | PropertyDecorator;
98
```
99
100
```typescript
101
@injectable()
102
class Ninja {
103
constructor(
104
@inject("Weapon") @named("primary") private primaryWeapon: IWeapon,
105
@inject("Weapon") @named("secondary") private secondaryWeapon: IWeapon
106
) {}
107
}
108
109
// Binding configuration
110
container.bind<IWeapon>("Weapon").to(Katana).whenTargetNamed("primary");
111
container.bind<IWeapon>("Weapon").to(Shuriken).whenTargetNamed("secondary");
112
```
113
114
### @tagged
115
116
Associates key-value metadata with an injection point for conditional resolution.
117
118
```typescript { .api }
119
function tagged(key: string, value: any): ParameterDecorator | PropertyDecorator;
120
```
121
122
```typescript
123
@injectable()
124
class Ninja {
125
constructor(
126
@inject("Weapon") @tagged("type", "melee") private meleeWeapon: IWeapon,
127
@inject("Weapon") @tagged("type", "ranged") private rangedWeapon: IWeapon,
128
@inject("Weapon") @tagged("power", "high") private powerWeapon: IWeapon
129
) {}
130
}
131
132
// Binding configuration
133
container.bind<IWeapon>("Weapon").to(Katana).whenTargetTagged("type", "melee");
134
container.bind<IWeapon>("Weapon").to(Bow).whenTargetTagged("type", "ranged");
135
container.bind<IWeapon>("Weapon").to(FireSword).whenTargetTagged("power", "high");
136
```
137
138
### @optional
139
140
Marks a dependency as optional, preventing injection errors if no binding is found.
141
142
```typescript { .api }
143
function optional(): ParameterDecorator | PropertyDecorator;
144
```
145
146
```typescript
147
@injectable()
148
class Ninja {
149
constructor(
150
@inject("Weapon") private weapon: IWeapon,
151
@inject("Shield") @optional() private shield?: IShield,
152
@inject("Logger") @optional() private logger?: ILogger
153
) {}
154
}
155
156
// Property injection with optional
157
@injectable()
158
class Warrior {
159
@inject("Weapon")
160
private weapon!: IWeapon;
161
162
@inject("Shield") @optional()
163
private shield?: IShield;
164
}
165
```
166
167
## Inheritance Decorators
168
169
### @injectFromBase
170
171
Inherits injection metadata from the base class for the same parameter position.
172
173
```typescript { .api }
174
function injectFromBase(): ParameterDecorator;
175
```
176
177
```typescript
178
@injectable()
179
class BaseWarrior {
180
constructor(@inject("Weapon") protected weapon: IWeapon) {}
181
}
182
183
@injectable()
184
class Ninja extends BaseWarrior {
185
constructor(
186
@injectFromBase() weapon: IWeapon,
187
@inject("Armor") private armor: IArmor
188
) {
189
super(weapon);
190
}
191
}
192
```
193
194
### @injectFromHierarchy
195
196
Inherits injection metadata from any class in the inheritance hierarchy.
197
198
```typescript { .api }
199
function injectFromHierarchy(): ParameterDecorator;
200
```
201
202
```typescript
203
@injectable()
204
class BaseEntity {
205
constructor(@inject("Logger") protected logger: ILogger) {}
206
}
207
208
@injectable()
209
class BaseWarrior extends BaseEntity {
210
constructor(
211
@injectFromHierarchy() logger: ILogger,
212
@inject("Weapon") protected weapon: IWeapon
213
) {
214
super(logger);
215
}
216
}
217
218
@injectable()
219
class Ninja extends BaseWarrior {
220
constructor(
221
@injectFromHierarchy() logger: ILogger,
222
@injectFromHierarchy() weapon: IWeapon,
223
@inject("Armor") private armor: IArmor
224
) {
225
super(logger, weapon);
226
}
227
}
228
```
229
230
### @unmanaged
231
232
Excludes a constructor parameter from dependency injection, requiring manual value provision.
233
234
```typescript { .api }
235
function unmanaged(): ParameterDecorator;
236
```
237
238
```typescript
239
@injectable()
240
class Ninja {
241
constructor(
242
@inject("Weapon") private weapon: IWeapon,
243
@unmanaged() private name: string,
244
@unmanaged() private level: number
245
) {}
246
}
247
248
// Manual instantiation required for unmanaged parameters
249
const ninja = new Ninja(weapon, "Hanzo", 10);
250
```
251
252
## Lifecycle Decorators
253
254
### @postConstruct
255
256
Marks a method to be called after the object is fully constructed and all dependencies are injected.
257
258
```typescript { .api }
259
function postConstruct(target: any, propertyKey: string): void;
260
```
261
262
```typescript
263
@injectable()
264
class Ninja {
265
@inject("Weapon") private weapon!: IWeapon;
266
@inject("Armor") private armor!: IArmor;
267
268
private isReady = false;
269
270
@postConstruct()
271
private initialize() {
272
this.weapon.sharpen();
273
this.armor.polish();
274
this.isReady = true;
275
console.log("Ninja is ready for battle!");
276
}
277
278
fight() {
279
if (!this.isReady) {
280
throw new Error("Ninja not initialized");
281
}
282
return this.weapon.attack();
283
}
284
}
285
```
286
287
### @preDestroy
288
289
Marks a method to be called before the object is destroyed or the container is disposed.
290
291
```typescript { .api }
292
function preDestroy(target: any, propertyKey: string): void;
293
```
294
295
```typescript
296
@injectable()
297
class DatabaseService {
298
@inject("Connection") private connection!: IConnection;
299
300
@preDestroy()
301
private cleanup() {
302
console.log("Closing database connection...");
303
this.connection.close();
304
}
305
}
306
```
307
308
## Programmatic Decoration
309
310
### decorate
311
312
Applies decorators programmatically instead of using decorator syntax.
313
314
```typescript { .api }
315
function decorate(
316
decorator: ClassDecorator | ParameterDecorator | PropertyDecorator,
317
target: any,
318
propertyKey?: string | symbol,
319
parameterIndex?: number
320
): void;
321
```
322
323
```typescript
324
class Ninja {
325
constructor(private weapon: IWeapon, private armor: IArmor) {}
326
}
327
328
// Apply decorators programmatically
329
decorate(injectable(), Ninja);
330
decorate(inject("Weapon"), Ninja, 0);
331
decorate(inject("Armor"), Ninja, 1);
332
333
// For properties
334
class Warrior {
335
private weapon!: IWeapon;
336
}
337
338
decorate(injectable(), Warrior);
339
decorate(inject("Weapon"), Warrior.prototype, "weapon");
340
```
341
342
## Decorator Combination Examples
343
344
### Complex Injection Scenarios
345
346
```typescript
347
@injectable()
348
class AdvancedNinja {
349
constructor(
350
// Primary weapon - required
351
@inject("Weapon") @named("primary")
352
private primaryWeapon: IWeapon,
353
354
// Secondary weapons - array injection
355
@multiInject("Weapon") @tagged("category", "secondary")
356
private secondaryWeapons: IWeapon[],
357
358
// Optional shield
359
@inject("Shield") @optional()
360
private shield?: IShield,
361
362
// Configuration - unmanaged
363
@unmanaged()
364
private config: NinjaConfig
365
) {}
366
367
@postConstruct()
368
private setup() {
369
this.primaryWeapon.prepare();
370
this.secondaryWeapons.forEach(weapon => weapon.prepare());
371
}
372
373
@preDestroy()
374
private cleanup() {
375
this.primaryWeapon.cleanup();
376
this.secondaryWeapons.forEach(weapon => weapon.cleanup());
377
}
378
}
379
```
380
381
## Metadata Types
382
383
```typescript { .api }
384
type MetadataName = string | number | symbol;
385
type MetadataTag = string | number | symbol;
386
387
interface ResolvedValueInjectOptions {
388
skipBaseClassChecks?: boolean;
389
}
390
391
interface ResolvedValueMetadataInjectOptions extends ResolvedValueInjectOptions {
392
key: MetadataName;
393
}
394
395
interface ResolvedValueMetadataInjectTagOptions extends ResolvedValueMetadataInjectOptions {
396
value: any;
397
}
398
```
399
400
## Container Operation Types
401
402
```typescript { .api }
403
type Bind<T> = (serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
404
type Unbind = (serviceIdentifier: ServiceIdentifier) => void;
405
type IsBound = (serviceIdentifier: ServiceIdentifier) => boolean;
406
type Rebind<T> = (serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
407
type RebindSync<T> = (serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
408
type UnbindSync = (serviceIdentifier: ServiceIdentifier) => void;
409
```
410
411
## Best Practices
412
413
1. **Always use @injectable**: Required on all classes participating in DI
414
2. **Prefer constructor injection**: More reliable than property injection
415
3. **Use specific service identifiers**: Avoid generic types like "string" or "number"
416
4. **Combine decorators thoughtfully**: Named and tagged decorators work together
417
5. **Handle optional dependencies**: Use @optional() for non-critical dependencies
418
6. **Leverage lifecycle hooks**: Use @postConstruct and @preDestroy for proper resource management