Javascript 带有typescript接口的Aurelia DI

Javascript 带有typescript接口的Aurelia DI,javascript,dependency-injection,aurelia,Javascript,Dependency Injection,Aurelia,我已经阅读了Aurelia DI的文档,并查看了源代码,希望与大家分享我试图实现的目标,这样,如果我遗漏了一些明显的东西,我就可以被击倒。我已经看过了Aurelia TS的样本,但我看不出它将如何工作,并且缺少文档 我想要的是: dataProvider.js(数据提供程序接口) itemDisplayer1.js(一个类,它将使用实现接口的注入类) itemDisplayer2.js(另一个类将使用实现接口的注入类) GoodDataProvider.js import {DataProvid

我已经阅读了Aurelia DI的文档,并查看了源代码,希望与大家分享我试图实现的目标,这样,如果我遗漏了一些明显的东西,我就可以被击倒。我已经看过了Aurelia TS的样本,但我看不出它将如何工作,并且缺少文档

我想要的是:

dataProvider.js(数据提供程序接口)

itemDisplayer1.js(一个类,它将使用实现接口的注入类)

itemDisplayer2.js(另一个类将使用实现接口的注入类)

GoodDataProvider.js

import {DataProvider} from "./dataProvider";

export class GoodDataProvider implements DataProvider {
  data = 1;
  getData() {
    return this.data;
  }
}
import {DataProvider} from "./dataProvider";

export class BetterDataProvider implements DataProvider {
  data = 2;
  getData() {
    return this.data;
  }
}
BetterDataProvider.js

import {DataProvider} from "./dataProvider";

export class GoodDataProvider implements DataProvider {
  data = 1;
  getData() {
    return this.data;
  }
}
import {DataProvider} from "./dataProvider";

export class BetterDataProvider implements DataProvider {
  data = 2;
  getData() {
    return this.data;
  }
}
然后在某个地方(?)我想配置itemDisplayer1应该提供GoodDataProvider的实例,itemDisplayer2应该提供BetterDataProvider(1)的实例

然后是DI上下文的问题。我不知道如何使用container.createChild()。我找不到太多关于它的信息。它创建一个子容器,并在需要时将委托回父容器,但是如果我创建2个子容器并向每个子容器注册2个提供者中的一个,itemDisplayer类如何知道使用哪一个(而不更改其定义并注入父容器等)

注意:生命周期管理信息不存在于依赖项的使用者或提供者中(这通常在Aurelia DI示例中完成,似乎有点做作)。我希望当消费者和提供者与上面的点(1)相关联时,能够定义这一点

总之,这可能吗?这是不是在不久的将来可能发生的事情?我是否应该尝试用满足我需求的定制容器替换Aurelia DI


(我尝试这样做的原因是,为了评估js框架,这些框架需要展示一个成熟的DI系统,将生命周期管理/AOP等功能作为标准之一)

来自@eisenbergeffect:DI将在编写基准后进行一些内部检修

但与此相关的是,它不能与接口一起工作,因为TypeScript会在运行时编译这些接口

当您在DI容器中注册不同的类型时,您必须提供唯一的键,然后在@Inject(xxx)语句中指定适当的唯一键。钥匙可以是你喜欢的任何东西。通常人们使用类型本身作为唯一键(这会引起一些混淆),但您可以使用字符串、数字或其他任何您喜欢的东西


单元测试也提供了信息:

正如Mike所说,Aurelia还不支持这种依赖解析特性。接口被编译掉,因此不能用作键(例如,
container.registerInstance(ISomething,newconcretesomething());

然而,有一个小技巧可以让你看起来像是在使用接口本身作为键

foo.ts:

export interface IFoo {
  // interface
}

export const IFoo = Symbol();
bar.ts:

import {IFoo} from "./foo.ts";

export class Bar implements IFoo {
  // implementation
}
main.ts:

import {IFoo} from "./foo.ts";
import {Bar} from "./bar.ts";

...

container.registerInstance(IFoo, new Bar());

...
import { IFoo } from "./ifoo.ts";
import { Bar } from "./bar.ts";

...

container.registerInstance(IFoo, new Bar());

...

这可以很好地编译,并且编译器知道何时根据使用它的上下文使用正确的重复类型。

因此,正如其他人所说,TS总是编译接口,目前没有办法使用纯接口。然而,TS的一个有趣且常被忽略的特性是,它允许使用
作为一个接口,这允许绕过当前限制

export abstract class DataProvider {
  getData(): number;
}

@singleton(DataProvider) // register with an alternative key
export class MyAwesomeDataProvider implements DataProvider {
}

@autoinject
export class DataConsumer {
  constructor(dataProvider: DataProvider) {
  }
}
在上面的代码中,我们声明了一个抽象类
DataProvider
,它将确保它不会被TS编译掉。然后,我们使用
DataProvider
的另一个
注册
MyAwesomeDataProvider
,每次
DataProvider时,它将返回
MyAwesomeDataProvider
的一个实例

对于子容器,您应该执行
container.createChild()
返回容器的新实例,只要从该子容器触发解析,您就应该得到正确的实例。唯一的问题是使用具有两个冲突键的装饰器。基本上,元数据存在于类本身,因此您不能让两个实例在
数据提供程序下注册e> ,这肯定会(尽管我自己没有试过)引起问题,唯一的办法就是使用显式注册

export abstract class DataProvider {
  getData(): number;
}

export class MyAwesomeDataProvider implements DataProvider {
}

export class MyMoreAwesomeDataProvider implements DataProvider {
}        

child1 = container.createChild();
child1.registerSingleton(DataProvider, MyAwesomeDataProvider);

child2 = container.createChild();
child2.registerSingleton(DataProvider, MyMoreAwesomeDataProvider);

@autoinject
export class DataConsumer {
  constructor(dataProvider: DataProvider) {
  }
}

child1.get(DataConsumer); // injects MyAwesomeDataProvider
child2.get(DataConsumer); // injects MyMoreAwesomeDataProvider
喜欢Frank Gambino的想法,并找到了一种方法使其既能与
@inject
一起工作,又能与
@autoinject
一起工作。诀窍是使用自定义接口(因为接口是在TypeScript中保留的,我称之为@I)

装饰部件:

myClass.ts

import { autoinject } from 'aurelia-framework';    
import { i } from './i.ts';
import { IFoo } from "./ifoo.ts";    

@autoinject
export class MyClass {
    constructor(@i(IFoo) foo: IFoo) {
        foo.doSomething();
    }
}
i、 ts:

其余部分与Frank Gambino的答案完全相同,但我添加它是为了完整性 …

ifoo.ts:

export interface IFoo {
    doSomething(): void;
}

export const IFoo = Symbol("IFoo"); // naming the symbol isn't mandatory, but it's easier to debug if something goes wrong
一些学生:

import { IFoo } from "./ifoo.ts";

export class Bar implements IFoo {
    doSomething(): void {
        console.log('it works!');
    }
}
main.ts:

import {IFoo} from "./foo.ts";
import {Bar} from "./bar.ts";

...

container.registerInstance(IFoo, new Bar());

...
import { IFoo } from "./ifoo.ts";
import { Bar } from "./bar.ts";

...

container.registerInstance(IFoo, new Bar());

...

它实际上可以与其他DI容器一起工作。要使它与Angular2一起工作(尽管你为什么要这样做?Aurelia更棒:),你只需要将i.ts文件中的
interfaceSymbol
的类型更改为
any
,而不是
Symobl(“IFoo”)
write
newinjectiontoken(“IFoo”)
(InjectionToken类是一个有角度的东西,遗憾的是,它们不支持符号作为注入令牌,至少目前是这样)。

我有一种不同的方法来解决这个问题,这对我来说很有效

参加以下课程:

export class Foo implements Bar {

}
我将此更改为以下内容:

import { Container } from 'aurelia-framework';

class Foo implements Bar {
}

export var foo = Container.instance.get(Foo) as Bar;
现在,我只需执行以下操作即可获得该类的类型化单例实例:

import { foo } from 'foo';

来自gitter频道的评论:但是在一个相关的注释中,它不能与接口一起工作,因为TypeScript会在运行时编译这些接口。谢谢你回复我。很高兴了解大修-知道什么时候(大致)会这样吗?我应该知道TS接口b