用于 TypeScript 的 IOC
Posted
技术标签:
【中文标题】用于 TypeScript 的 IOC【英文标题】:IOC for TypeScript 【发布时间】:2012-09-29 12:38:24 【问题描述】:现在有了 TypeScript,我们在 javascript 中有了静态分析和许多 OOP 功能。 所以现在是时候在客户端逻辑中进行更好的单元测试了,而且我们需要 IOC 容器来进行依赖注入,以使代码更具可测试性......
那么,有人已经体验过这个主题,或者可能知道可以移植到 TypeScript 的 typescript 或 JavaScript 框架的库吗?
【问题讨论】:
在运行时,没有 TypeScript;只有Javascript。所以它们在运行时不是静态类型系统,而对于 IoC,我们需要一个运行时类型系统。 是的,我知道,但是运行时也没有 c#|Delphi 等,并且已经有使用 SourceMap (***.com/questions/12711826/…) 调试 TypeScript 的方法。 确实如此,但在 .NET 中,我们可以通过使用反射来确定构造函数需要哪些类型。此类功能不适用于 javascript。 【参考方案1】:目前,您可以在没有 IOC 部分的 JavaScript 中使用依赖注入。是否编写“手动”解析器、工厂或任何您喜欢的 DI 模式取决于您。
当采用 ECMAScript 6 标准时,可能会使 IOC 的概念在 JavaScript 中成为可能。
【讨论】:
【参考方案2】:我们一直在使用一个简单的依赖注入容器,它使用 AMD 定义/要求 - 类似语法。最初的实现是在 TypeScript 中,尽管下面的博客文章用普通的旧 JavaScript 呈现它。
http://blog.coolmuse.com/2012/11/11/a-simple-javascript-dependency-injection-container/
定义依赖关系非常简单,不需要一堆配置,并且支持类似于requirejs的循环依赖解析。
这是一个简单的例子:
// create the kernel
var kernel = new ServiceKernel();
// define service1
kernel.define("service1", function()
// service1 has a function named foo
return
foo: function () return "foo";
);
// define service2, which depends on service1
kernel.define("service2", ["service1"], function(service1)
// service2 has a function named foobar
return
foobar : function() return service1.foo() + "bar";
);
// get service2 instance
var service2 = kernel.require("service2");
service2.foobar(); // returns "foobar"
// get both service1 and service2 instances
kernel.require(["service1", "service2"], function(service1, service2)
alert(service1.foo() + service2.foobar()); // displays foofoobar
);
【讨论】:
【参考方案3】:我开发了一个名为 InversifyJS 的 IoC 容器,它具有上下文绑定等高级依赖注入功能。
您需要遵循3个基本步骤才能使用它:
1。添加注释
注解 API 基于 Angular 2.0:
import injectable, inject from "inversify";
@injectable()
class Katana implements IKatana
public hit()
return "cut!";
@injectable()
class Shuriken implements IShuriken
public throw()
return "hit!";
@injectable()
class Ninja implements INinja
private _katana: IKatana;
private _shuriken: IShuriken;
public constructor(
@inject("IKatana") katana: IKatana,
@inject("IShuriken") shuriken: IShuriken
)
this._katana = katana;
this._shuriken = shuriken;
public fight() return this._katana.hit(); ;
public sneak() return this._shuriken.throw(); ;
2。声明绑定
绑定API基于Ninject:
import Kernel from "inversify";
import Ninja from "./entities/ninja";
import Katana from "./entities/katana";
import Shuriken from "./entities/shuriken";
var kernel = new Kernel();
kernel.bind<INinja>("INinja").to(Ninja);
kernel.bind<IKatana>("IKatana").to(Katana);
kernel.bind<IShuriken>("IShuriken").to(Shuriken);
export default kernel;
3。解决依赖关系
解析API基于Ninject:
import kernel = from "./inversify.config";
var ninja = kernel.get<INinja>("INinja");
expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true
最新版本 (2.0.0) 支持许多用例:
内核模块 内核中间件 使用类、字符串文字或符号作为依赖标识符 注入常量值 注入类构造函数 注入工厂 汽车厂 提供程序注入(异步工厂) 激活处理程序(用于注入代理) 多次注射 标记绑定 自定义标签装饰器 命名绑定 上下文绑定 友好的例外(例如循环依赖)您可以通过https://github.com/inversify/InversifyJS了解更多信息
【讨论】:
我们是否总是需要第 2 步绑定? 绑定是必要的,但是有一个实用程序可以为您声明绑定github.com/inversify/inversify-binding-decorators 这个可以不用TypeScript吗? 是的!查看github.com/inversify/InversifyJS/blob/master/wiki/… 和github.com/inversify/inversify-vanillajs-helpers inversify 很复杂,一直在玩,但它带走了js的乐趣【参考方案4】:我已经为 typescript 创建了 DI 库 - huject
https://github.com/asvetliakov/huject
例子:
import Container, FactoryMethod, ConstructorInject, Inject from 'huject';
class FirstService
public constructor(param: number)
class SecondService
@ConstructorInject
class MyController
@Inject
public service: FirstService;
public second: SecondService;
public constructor(service: SecondService)
this.second = service;
...
container.setAllowUnregisteredResolving(true);
container.register(FirstService, [10]); // Register constructor arguments
// FirstService and SecondService will be resolved for container instance
let controller = container.resolve(MyController);
TypeScript 接口存在问题,但我有 2 个解决方法(使用抽象类或简单类作为接口)
【讨论】:
谢谢,哦,伙计,我不敢相信没有人创建了一个可以很好地与 typescript 配合使用的适当的依赖注入系统(很像 Angualr2 DI)。感谢您创建它,它很可爱,已将其添加到我的项目中 这就是我要找的东西!反转被夸大了【参考方案5】:或者,您可以不使用任何框架,而是使用类作为容器,将对象工厂作为属性。然后,您可以在测试中继承此类并更改工厂。这种方法是类型安全的,不需要任何装饰器,只需注册类。
class B
echo()
alert('test');
class A
constructor(private b: B)
b.echo();
class Container
A = () => new A(this.B());
B = singleton(() => new B());
var c = new Container();
var a = c.A();
// singleton helper:
function memoize<T>(factory: () => T): () => T
var memo: T = null;
return function ()
if(!memo)
memo = factory();
return memo;
;
var singleton = memoize;
【讨论】:
【参考方案6】:结帐https://github.com/typestack/typedi
这样的事情是可能的:
import "reflect-metadata";
import Service, Container from "typedi";
@Service()
class SomeClass
someMethod()
let someClass = Container.get(SomeClass);
someClass.someMethod();
【讨论】:
【参考方案7】:我曾经在中小型项目中使用TSyringe。它工作得很好,但我的结论是我不需要在 JavaScript / TypeScript 中进行依赖注入。
由于 JavaScript 是一种动态语言,所有实现都可以在运行时更改。如果您只想注入存根对象进行单元测试,请使用SinonJS 或类似工具。
为什么:
它会给您的代码带来不必要的复杂性和冗长的样板代码。 一切都应该写成 Class,因此 TypeScript 无法为某些方法推断正确的类型,这意味着您应该手动为它们键入参数。 大多数依赖注入框架依赖于 TypeScript 装饰器,编译速度很慢。该功能仍处于试验阶段(可能会随着时间的推移而改变)【讨论】:
以上是关于用于 TypeScript 的 IOC的主要内容,如果未能解决你的问题,请参考以下文章
typescript 小型TypeScript(JS)类,用于需要在页面上分开的可观察数组。