inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.17k stars 713 forks source link

inversify-binding-decorators fails with separated files #1253

Open tariano opened 3 years ago

tariano commented 3 years ago

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

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.

Elk-song commented 3 years ago

请问你解决了么?

erich-wittenbeck commented 7 months ago

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!