inversify-binding-decorators

原文链接

允许开发者使用ES2016装饰器来声明InversifyJS绑定的工具:

image

安装

您可以使用npm进行安装

npm install inversify inversify-binding-decorators reflect-metadata --save
1

inversify-binding-decorator类型定义包含在了npm模块中,TypeScript版本要求是2.0以上。请参阅InversifyJS文档以了解更多关于安装过程的信息。

基础

InversifyJS的API让我们声明绑定时更加流畅:

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

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

@injectable()
class Shuriken implements ThrowableWeapon {
    public throw() {
        return "hit!";
    }
}

var container = new Container();
container.bind<Katana>("Katana").to(Katana);
container.bind<Shuriken>("Shuriken").to(Shuriken);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

该库可以让您使用装饰器语法来声明绑定:

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

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

@provide(Shuriken)
class Shuriken implements ThrowableWeapon {
    public throw() {
        return "hit!";
    }
}

var container = new Container();
// Reflects all decorators provided by this package and packages them into a module to be loaded by the container
// 它代表了这个包所提供的所有装饰器,并将它们打包为一个模块以便容器加载。
container.load(buildProviderModule());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

多次使用@provide的情况

如果您尝试多次使用@provide

@provide("Ninja")
@provide("SilentNinja")
class Ninja {
    // ...
}
1
2
3
4
5

该库会抛出一个错误:

Cannot apply @injectable decorator multiple times. Please use @provide(ID, true) if you are trying to declare multiple bindings!

我们这么做是防止您误操作多次使用@provide装饰器:

您可以通过将第二个参数force设置为true来绕过这个检测:

@provide("Ninja", true)
@provide("SilentNinja", true)
class Ninja {
    // ...
}
1
2
3
4
5

使用类、字符串和symbol作为标识符

当您用类作为标识符来调用@provide时:

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

@provide(Ninja)
class Ninja {
    private _katana: Weapon;
    public constructor(
        katana: Weapon
    ) {
        this._katana = katana;
    }
    public fight() { return this._katana.hit(); };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

会在暗处创建一个新的绑定:

container.bind<Katana>(Katana).to(Katana);
container.bind<Ninja>(Ninja).to(Ninja);
1
2

除了类,使用字符串作为标识符也是可以的:

let TYPE = {
    IKatana: "Katana",
    INinja: "Ninja"
};

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

@provide(TYPE.Ninja)
class Ninja implements Ninja {

    private _katana: Weapon;

    public constructor(
        @inject(TYPE.Katana) katana: Weapon
    ) {
        this._katana = katana;
    }

    public fight() { return this._katana.hit(); };

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

还可以使用Symbol作为标识符:

let TYPE = {
    Katana: Symbol("Katana"),
    Ninja: Symbol("Ninja")
};

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

@provide(TYPE.Ninja)
class Ninja implements Ninja {

    private _katana: Weapon;

    public constructor(
        @inject(TYPE.Katana) katana: Weapon
    ) {
        this._katana = katana;
    }

    public fight() { return this._katana.hit(); };

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

连续绑定装饰器

基本的@provide装饰器并不允许声明上下文约束、作用域和其他高级绑定功能。为此,我们提供了另一个装饰器,它允许您使用连续的语法绑定所有的功能:

import { injectable, Container } from "inversify";
import { fluentProvide, buildProviderModule } from "inversify-binding-decorators";

let TYPE = {
    Weapon : "Weapon",
    Ninja: "Ninja"
};

@fluentProvide(TYPE.Weapon).whenTargetTagged("throwable", true).done();
class Katana implements Weapon {
    public hit() {
        return "cut!";
    }
}

@fluentProvide(TYPE.Weapon).whenTargetTagged("throwable", false).done();
class Shuriken implements Weapon {
    public hit() {
        return "hit!";
    }
}

@fluentProvide(TYPE.Ninja).done();
class Ninja implements Ninja {

    private _katana: Weapon;
    private _shuriken: Weapon;

    public constructor(
        @inject(TYPE.Weapon) @tagged("throwable", false) katana: Weapon,
        @inject(TYPE.Weapon) @tagged("throwable", true) shuriken: ThrowableWeapon
    ) {
        this._katana = katana;
        this._shuriken = shuriken;
    }

    public fight() { return this._katana.hit(); };
    public sneak() { return this._shuriken.throw(); };

}

var container = new Container();
container.load(buildProviderModule());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

您可以为连续装饰器创建别名来满足您的需求:

let provideThrowable = function(identifier, isThrowable) {
	return provide(identifier)
		      .whenTargetTagged("throwable", isThrowable)
		      .done();
};

@provideThrowable(TYPE.Weapon, true)
class Katana implements Weapon {
    public hit() {
        return "cut!";
    }
}

@provideThrowable(TYPE.Weapon, false)
class Shuriken implements Weapon {
    public hit() {
        return "hit!";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

另一个示例:

let provideSingleton = function(identifier) {
	return provide(identifier)
		      .inSingletonScope()
		      .done();
};

@provideSingleton(TYPE.Weapon)
class Shuriken implements Weapon {
    public hit() {
        return "hit!";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

多次使用@fluentProvide装饰器

如果多次使用@fluentProvide装饰器:

let container = new Container();

const provideSingleton = (identifier: any) => {
    return fluentProvide(identifier)
        .inSingletonScope()
        .done();
};

function shouldThrow() {
    @provideSingleton("Ninja")
    @provideSingleton("SilentNinja")
    class Ninja {}
    return Ninja;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

同样我们会抛出一个错误:

Cannot apply @fluentProvide decorator multiple times but is has been used multiple times in Ninja Please use done(true) if you are trying to declare multiple bindings!

我们这么做是防止您误操作多次使用@fluentProvide装饰器:

您可以通过向done()方法传递true来绕过这个检测:

const provideSingleton = (identifier: any) => {
    return fluentProvide(identifier)
    .inSingletonScope()
    .done(true); // IMPORTANT! 重要!
};

function shouldThrow() {
    @provideSingleton("Ninja")
    @provideSingleton("SilentNinja")
    class Ninja {}
    return Ninja;
}
let container = new Container();
container.load(buildProviderModule());
1
2
3
4
5
6
7
8
9
10
11
12
13
14

自动provide工具

本库提供了一个小的工具,方便您向模块的所有公共属性添加默认的@provide装饰器:

考虑一下下面这个例子:

import * as entites from "../entities";

let container = new Container();
autoProvide(container, entites);
let warrior = container.get(entites.Warrior);
expect(warrior.fight()).eql("Using Katana...");
1
2
3
4
5
6

entities.ts文件的内容类似下面这种:

export { default as Warrior } from "./warrior";
export { default as Katana } from "./katana";
1
2

katana.ts文件长这样:

class Katana {
    public use() {
        return "Using Katana...";
    }
}

export default Katana;
1
2
3
4
5
6
7

warrior.ts文件长这样:

import Katana from "./katana";
import { inject } from "inversify";

class Warrior {
    private _weapon: Weapon;
    public constructor(
        // we need to declare binding because auto-provide uses @injectable decorator at runtime not compilation time
        // in the future maybe this limitation will desapear thanks to design-time decorators or some other TS feature

        // 因为auto-provide是在运行时而非编译时使用的@injectable装饰器,因此我们需要声明绑定
        @inject(Katana) weapon: Weapon
    ) {
        this._weapon = weapon;
    }
    public fight() {
        return this._weapon.use();
    }
}
export default Warrior;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
上次更新: 1/20/2020, 11:13:37 AM