InversifyJS icon indicating copy to clipboard operation
InversifyJS copied to clipboard

inversify-binding-decorators fails with separated files

Open tariano opened this issue 5 years ago • 2 comments

Expected Behavior

I expect to see success emitted to console in code below

Current Behavior

Instead, I see the error Error: No matching bindings found for serviceIdentifier: Weapon

Steps to Reproduce (for bugs)

Just run the snippet below

Context

I'm trying to use inversify-binding-decorators to get rid of boilerplate.

Your Environment

  • Version used: "inversify": "^5.0.1", "inversify-binding-decorators": "^4.0.0", "reflect-metadata": "^0.1.13"
  • Environment name and version: node.js: v14.8.0
  • Operating System and version: Windows 10
  • Link to your project: N/A - see code below
  • Running in debug mode using PyCharm, if it helps..

Detailed description:

The following code was working very well, until I separated it to different files:

import {Container, inject} from "inversify";
import {buildProviderModule, provide} from "inversify-binding-decorators";
import "reflect-metadata";

export interface Weapon {
    hit(): string
}

@provide("Weapon")
export class Katana implements Weapon {
    public hit() {
        return "cut!";
    }
}

@provide(Ninja)
export class Ninja {
    private weapon: Weapon;

    constructor(@inject("Weapon") weapon: Weapon) {
        this.weapon = weapon;
    }

    Fight() {
        return this.weapon.hit()
    }
}


try {
    const container = new Container();
    container.load(buildProviderModule());
    const ninja = container.get(Ninja);

    console.log(ninja.Fight())
    console.log("success")
} catch (e) {
    console.error(e)
}

console.log("done")

After refactoring, this is the new file structure:

ioc/
    node_modules/
    src/
        warrior.ts
        weapon.ts
    main.ts
    package.json
    package-lock.json
    tsconfig.json

They hold the same logic, just separated:

warrior.ts:

import "reflect-metadata";
import {provide} from "inversify-binding-decorators";
import {inject} from "inversify";
import {Weapon} from "./weapon";

@provide(Ninja)
export class Ninja {
    private weapon: Weapon;

    constructor(@inject("Weapon") weapon: Weapon) {
        this.weapon = weapon;
    }

    Fight() {
        return this.weapon.hit()
    }
}

weapon.ts:

import {provide} from "inversify-binding-decorators";

export interface Weapon {
    hit(): string
}

@provide("Weapon")
export class Katana implements Weapon {
    public hit() {
        return "cut!";
    }
}

main.ts:

import {Container} from "inversify";
import {buildProviderModule} from "inversify-binding-decorators";
import "reflect-metadata";
import {Ninja} from "./src/warrior";

try {
    const container = new Container();
    container.load(buildProviderModule());
    const ninja = container.get(Ninja);

    console.log(ninja.Fight())
    console.log("success")
} catch (e) {
    console.error(e)
}

console.log("done")

But now it fails on const ninja = container.get(Ninja); with error Error: No matching bindings found for serviceIdentifier: Weapon.

tariano avatar Oct 01 '20 15:10 tariano

请问你解决了么?

Elk-song avatar Jan 22 '21 09:01 Elk-song

In case anyone stumbles upon this: This is seems not to be an issue with the library itself, but with the way code is compiled and then evaluated during run time.

tariano's main.ts does not include any import of the Katana class or refererence to the weapon.ts file in general, which makes sense since it's supposed to be injected. However, this means the run time will execute the code in main.ts without ever touching Katana (as only the Weapon interface is referenced by the Ninja class) and thus it's provide decorator is never evaluated.

Unfortunately, there seems no real fix to this. Only the workaround of explicitly importing the module like so:

import './src/weapon';

... at the beginning of main.ts, in order to ensure the class is bound to the container before accessing it.

Still, I hope this helps!

erich-wittenbeck avatar Feb 13 '24 00:02 erich-wittenbeck