or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

binding.mdconditional.mdcontainer.mddecorators.mdindex.mdlifecycle.mdmodules.md
tile.json

decorators.mddocs/

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