thiagobustamante / typescript-ioc

A Lightweight annotation-based dependency injection container for typescript.
MIT License
526 stars 64 forks source link

Question about interfaces #20

Closed tmorell closed 6 years ago

tmorell commented 6 years ago

In the documentation you mentioned that interfaces cannot be used: https://github.com/thiagobustamante/typescript-ioc#a-note-about-classes-and-interfaces

I have not looked at the implementation details, but InversifyJS does allow using interfaces.

What is the key difference between the two frameworks?

thiagobustamante commented 6 years ago

Hi,

The problem is that Interface is a concept that exists in Typescript, but not in Javascript. When the compilation occurs, no code is generated for any interface. They are used only for type checking by the compiler and no information remains about them in the generated javascript code.

All IoC tools suffers because of this problem. As you can see here, this is an open issue in typescript without any conclusive solution. The Inversify creator is one of those that joined this thread trying to find out a solution for this isse (remojansen).

So, once we have this scenario, lets go to our current options:

The second approach is what Inversify use to solve it:

Look how it handle this:

interface Warrior {
    fight(): string;
}

interface Weapon {
    hit(): string;
}
let TYPES = {
    Warrior: Symbol("Warrior"),
    Weapon: Symbol("Weapon")
};
@injectable()
class Katana implements Weapon {
    public hit() {
        return "cut!";
    }
}

@injectable()
class Ninja implements Warrior {
    private _katana: Weapon;
    public constructor(
        @inject(TYPES.Weapon) katana: Weapon
    ) {
        this._katana = katana;
    }
    public fight() { return this._katana.hit(); };
}

So, as you can see, it uses a Symbol (could be a string too) to be used as the key for its type bindings. It needs something in runtime to be used to map the real type being injected.

Here, in typescript-ioc, we decide to not use any other key to map a type...

So, you can just inject anything without the need to create thinks like:

let TYPES = {
    Warrior: Symbol("Warrior"),
    Weapon: Symbol("Weapon")
};

and

        @inject(TYPES.Weapon) katana: Weapon

In typescript-ioc you don't need to pass anything to @Inject. The type being injected is enough information to decide the real type that must be resolved.

I think that keeps the usage simpler.... but the price is that you should replace your interfaces by abstract classes...