用于 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)类,用于需要在页面上分开的可观察数组。

Typescript:这种用于键入函数的键入方法是啥?

用于道具的 TypeScript 条件类型

TSLint 不适用于 CRA 和 TypeScript

将 : 和 => 用于 TypeScript 函数的返回类型有啥区别?

TypeScript - instanceof 不适用于 JSON