Umut Benzer Umut Benzer - 3 months ago 30
TypeScript Question

Binding classes with property injection to kernel in inversify

I am trying to achieve dependency injection on my node project via inversify.

I have a kernel config like this:

inversify.config.ts


import "reflect-metadata";
import {Kernel, interfaces} from "inversify";

import {Config} from "./config";
import {Collection} from "./collection";

let kernel = new Kernel();

kernel.bind<Config>("Config").to(Config).inSingletonScope();
// kernel.bind<interfaces.Newable<Collection>>("Collection").toConstructor<Collection>(Collection);
// it works without the line above


A class:
config.ts


import {injectable} from "inversify";
@injectable()
export class Config {
constructor() {}
}


A class with property DI
collection.ts


import {injectable} from "inversify";
import getDecorators from "inversify-inject-decorators";
import kernel from "../inversify.config";
let {lazyInject} = getDecorators(kernel);

@injectable()
export class Collection {
@lazyInject(Config)
private Config: Config;

constructor(a: string, b: string) {}
}


Everything works as expected if I don't bind a class with property injection. When I try to bind a class with
@lazyInject
as shown in the example

kernel.bind<interfaces.Newable<Collection>>("Collection").toConstructor<Collection>(Collection);


import line in
inversify.config.ts
starts processing
Collection.ts
and the line

import kernel from "../inversify.config";`


in it. However as we arrived
Collection.ts
while we already processing
inversify.config.ts
file line

import kernel from "../inversify.config";`


somehow returns
undefined
making kernel
undefined
for
Collection
class. Therefore
@lazyInject
DI fails.

Eventually when I try to read
Config
in
Collection
it fails with:

TypeError: Cannot read property 'get' of undefined
at resolve (node_modules/inversify-inject-decorators/lib/decorators.js:24:30)
at Category.getter (node_modules/inversify-inject-decorators/lib/decorators.js:6:47)


I wonder if there is a way to accomplish binding a class with property DI via
@lazyInject
without moving kernel definition into the same file with one of the classes. I am looking for a way to import kernel as it is now, but make it work properly.

Answer

Your problem is that you have a circular dependency:

enter image description here

I'm going to change your code and add a couple of additional files. Nothe that the he file types.ts is not needed but it is recommended to keep all your type identifiers in one single location.

After changing your code the dependency diagram will change to the following:

enter image description here

As you can se we have eliminated the circular dependency.

inversify.config.ts

import "reflect-metadata";
import {Kernel, interfaces} from "inversify";
import getDecorators from "inversify-inject-decorators";
import { makeProvideDecorator } from "inversify-binding-decorators";

let kernel = new Kernel();
let {lazyInject} = getDecorators(kernel);
let provide = makeProvideDecorator(kernel);

export { lazyInject, provide };

types.ts

let TYPES = {
  Config: "Config",
  Collection: "Collection",
};

export default TYPES;

config.ts

import { provide } from "../inversify.config";
import TYPES from "./types";

@provide(TYPES.Config)
export class Config {
  constructor() {}
}

export default Config;

collection.ts

import { lazyInject, provide } from "../inversify.config";

@provide(TYPES.Collection)
export class Collection {
  @lazyInject(TYPES.Config)
  private Config: Config;
  constructor(a: string, b: string) {}
}

export default Collection;

main.ts

You need to import all your dependencies when they are imported @provide is executed and then bindings are generated.

import Config from "./lib/config";
import Collection from "./models/collection";

//...

We have been able to eliminate the circular dependency using the @provide decorator from inversify-binding-decorators.