定义全局 TypeScript 配置(接口),用于 Aurelia 依赖注入

Define global TypeScript config (interface), for use with Aurelia dependency injection

在 Aurelia 中,我有几个 classes 依赖于相同的配置。使用 IoC/DI,这个配置可以作为构造函数参数提供似乎很自然。例如:

@autoinject
export class CustomerService {
    constructor(config: IRemoteServiceConfig) {
    }
}

@autoinject
export class GummyBearService {
    constructor(config: IRemoteServiceConfig) {
    }
}

在最简单的示例中,IRemoteServiceConfig 可能看起来像这样(为简洁起见删除了其他内容):

export IRemoteServiceConfig {
    endpoint: string;
    apiKey?: string;
    // etc. several other settings
}

在构造函数中注入配置非常适合测试,不需要我阅读每个 class 中的配置和设置。

这些服务依赖于相同的配置,我 想在我的应用程序启动期间定义一次 -

通读 Aurelia docs on dependency injection,我发现有几种方法可用于此目的,例如 registerInstance()registerResolver()registerSingleton()。文档确实缺少关于 howwhere 的一些上下文来定义它。

我在启动例程的 configure() 部分开始使用类似以下的内容:

// register a static config; for brevity these are hardcoded settings
// but could come from anywhere
container.registerSingleton(IRemoteServiceConfig, () => {
    return <IRemoteServiceConfig> {
        endpoint: 'http://foo.com/api/v23/',
        apiKey: 'abc'
    }
});

它只是似乎没有拾取任何东西(没有错误)。但这也可能只是我对如何初始化容器的无知。

我的问题:我如何以及在哪里可以在 Aurelia 中定义 IRemoteServiceConfig(如果有的话),这样一旦 DI 启动服务,它就会自动获取我的(硬编码的) ) 配置?

注意this SO question中提到"it can’t work with interfaces because TypeScript compiles those away at runtime."。这个问题也有 2 年历史了,在 Aurelia 和 TypeScript 中都发生了很多变化。不管情况是否仍然如此,同样的问题也适用于 class 的实例而不是接口。

另请注意,我知道 aurelia-configuration 等库似乎适合将应用程序设置存储在配置文件中(并且工作正常)。这对于给出的示例更有意义。但是这个问题纯粹与我如何定义要由 Aurelia DI 解析的 class 的接口或特定实例有关。

无论使用哪个 TypeScript 版本,接口在运行时都不存在。

为了同时用作接口和 DI 令牌,IRemoteServiceConfig 应该是 class:

export abstract class IRemoteServiceConfig { ... }

@estus提供的答案是正确的,我已将其标记为答案。虽然,我确实遇到了一些额外的问题来让它正常工作。为了指出这一点,我花时间在下面写了一个包含更多详细信息的答案,希望它能对以后的其他人有所帮助。

1.) 使用 class,而不是接口
与接受的答案状态一样,您不能使用接口 - 它必须是 class.

2.) 通过aurelia.container
访问全局容器实例 文档没有提到如何获取容器的实例。但是你可以通过aurelia.containerconfigure()操作中直接调用default/global容器,像这样:

aurelia.container.registerSingleton(RemoteServiceConfig, () => {
  var config = new RemoteServiceConfig();
    config.endpoint = 'http://foo.com/api/v23/',
    config.apiKey = 'abc'
});

我在初始化 Container() 的新实例时出错,还试图以某种方式将其注入到 configure() 操作中。我不应该 :)

3.) 将您的 class 拆分到不同的文件中
这看起来很愚蠢但很重要:classes 不能在同一个文件中,否则你会得到以下错误:

key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?

出于原始问题的目的,我在同一个 app.ts 文件中创建了所有 classes。这似乎根本行不通。

综上所述,完整的应用程序现在看起来像这样并且可以运行:

main.ts

import { Aurelia } from 'aurelia-framework'
import { RemoteServiceConfig } from './app';

export function configure(aurelia: Aurelia) {
  aurelia.use
    .standardConfiguration()
    .feature('resources');

  aurelia.container.registerSingleton(RemoteServiceConfig, () => {
    var config = new RemoteServiceConfig();
      config.endpoint = 'http://foo.com/api/v23/',
      config.apiKey = 'abc'
  });

  aurelia.start().then(() => aurelia.setRoot());
}

app.ts

import { autoinject } from 'aurelia-dependency-injection';

// note: classes below should be 3 different files!

@autoinject
export class App {
  constructor(private service: CustomerService) {
  }
}

@autoinject
export class CustomerService {
  constructor(private config: RemoteServiceConfig) {
  }
}

export class RemoteServiceConfig {
  public endpoint: string;
  public apiKey?: string;
}