Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my property injection attempt not injecting anything but undefined?

I have an existing typescript project using inversify. I have defined a logger in my TYPES at TYPES.ILoggger and when I access the logger directly from my container it works:

import {ILogger} from "./interfaces/ILogger";
import {TYPES} from "./interfaces/TYPES";
import container from "./kernel/inversify.config";

const loggerFromTheContainer: ILogger = container.get<ILogger>(TYPES.ILogger);
loggerFromTheContainer.info("I WILL LOG"); // this works

So my setup should be fine.

I don't want to access the container directly but I want to use property injection. [InversifyJS' README provides an example:

If you prefer it you can use property injection instead of constructor injection so you don't have to declare the class constructor:

@injectable()
class Ninja implements Warrior {
    @inject(TYPES.Weapon) private _katana: Weapon;
    @inject(TYPES.ThrowableWeapon) private _shuriken: ThrowableWeapon;
    public fight() { return this._katana.hit(); }
    public sneak() { return this._shuriken.throw(); }
}

and I try to follow that one.

Yet when I try to inject the logger through property injection, I suddenly get undefined as a logger and I don't know why:

@injectable()
class ShouldHaveLogger {
    @inject(TYPES.ILogger) private logger: ILogger;

    constructor() {
        this.logger.info("Hello World"); // this.logger will remain undefined
    }
}

new ShouldHaveLogger();

It will throw a generic

TypeError: Cannot read property 'info' of undefined

as this.logger is not being injected. Yet why and how to fix it?

like image 206
k0pernikus Avatar asked Jan 30 '19 10:01

k0pernikus


1 Answers

The normal behavior of property injection is to let inversify create the instances for you. You are now describing a use-case, in which you create the instance of ShouldHaveLogger yourself (or another library is creating it for you).

This is also pointed out by the docs

In order to do that, you will need inversify-inject-decorators, as then you can inject the logger e.g. via @lazyInject.

You have to setup lazyInject using your container and your code should look like:

import {injectable} from "inversify";
import getDecorators from "inversify-inject-decorators";
import {ILogger} from "./interfaces/ILogger";
import {TYPES} from "./interfaces/TYPES";
import container from "./kernel/inversify.config";   

const {lazyInject} = getDecorators(container);

@injectable()
class ShouldHaveLogger {
    @lazyInject(TYPES.ILogger) private logger: ILogger;

    constructor() {
        this.logger.info("I am working now");
    }
}

new ShouldHaveLogger();

Will print depending on you logger implementation:

2019-01-30T09:47:54.890Z - info: "I am working now"
like image 185
k0pernikus Avatar answered Sep 23 '22 00:09

k0pernikus