typescriptdependency-injectionsingletoninversifyjs

Inversify circular singleton injection


I'm trying to use two singletons and make them able to call each other like this

import 'reflect-metadata';
import { Container, inject, injectable } from 'inversify';

let container = new Container();

@injectable()
class Dom {
  private domUi: DomUi;

  constructor (domUi: DomUi) {
    this.domUi = domUi;
  }
}

@injectable()
class DomUi {
  private dom: Dom;

  constructor (dom: Dom) {
    this.dom = dom;
  }
}

@injectable()
class Test {
  constructor (dom: Dom) {
    console.log(dom);
  }
}

container.bind<Dom>(Dom).toSelf().inSingletonScope();
container.bind<DomUi>(DomUi).toSelf().inSingletonScope();

const test = container.resolve(Test);

But it gives this error

Error: Missing required @inject or @multiInject annotation in: argument 0 in class Dom.

How can this be fixed? I tried the @inject and @multiInject with no luck!
is there a better way to think about this from a design pattern standpoint?


Solution

  • I think you have found a bug, you can use the following as a workaround:

    import "reflect-metadata";
    import { Container, inject, injectable } from "inversify";
    import getDecorators from "inversify-inject-decorators";
    
    let container = new Container();
    let { lazyInject } = getDecorators(container);
    
    @injectable()
    class DomUi {
        private _dom: Dom;
    
        public constructor (dom: Dom) {
            this._dom = dom;
        }
    }
    
    @injectable()
    class Dom {
        @lazyInject(DomUi) private domUi: DomUi;
    }
    
    @injectable()
        class Test {
        constructor(dom: Dom) {
            console.log(dom);
        }
    }
    
    container.bind<Dom>(Dom).toSelf().inSingletonScope();
    container.bind<DomUi>(DomUi).toSelf().inSingletonScope();
    const dom = container.resolve(Test);
    

    The recommended implementation would be with symbols not classes as IDs:

    import "reflect-metadata";
    import { Container, inject, injectable } from "inversify";
    import getDecorators from "inversify-inject-decorators";
    
    let container = new Container();
    let { lazyInject } = getDecorators(container);
    
    const TYPE = {
        Dom: Symbol("Dom"),
        DomUi: Symbol("DomUi"),
        Test: Symbol("Test")
    };
    
    interface Dom {}
    interface DomUi {}
    interface Test {}
    
    @injectable()
    class DomUi {
        public dom: Dom;
    
        public constructor (
            @inject(TYPE.Dom) d: Dom
        ) {
            this.dom = d;
        }
    }
    
    @injectable()
    class Dom {
        @lazyInject(TYPE.DomUi) public domUi: DomUi;
    }
    
    @injectable()
    class Test {
        constructor(
            @inject(TYPE.Dom) d: Dom
        ) {
            console.log(d, d.domUi.dom);
        }
    }
    
    container.bind<Dom>(TYPE.Dom).to(Dom).inSingletonScope();
    container.bind<DomUi>(TYPE.DomUi).to(DomUi).inSingletonScope();
    container.bind<Test>(TYPE.Test).to(Test);
    const dom = container.get(TYPE.Test);