8

我目前正在尝试清理一些代码,以便针对接口而不是针对实现进行编程,但不知道如何去做。

更具体地说,我正在使用: * TypeScript 1.5.0 beta -> 转换为 ES5 / commonjs * SystemJS 来加载模块

我目前尝试使用如下外部模块:

post.service.ts 文件:

///<reference path="../../../typings/tsd.d.ts" />
///<reference path="../../../typings/typescriptApp.d.ts" />
...
export interface PostsService{ // 1
    fetchPosts(): Rx.Observable<any>;
}
export var PostsService:PostsServiceImpl; // 2
...
export class PostsServiceImpl implements PostsService { // 3
    ...
    constructor(){
        console.log('Loading the Posts service');
}
...
fetchPosts(): Rx.Observable<any>{ 
   ...
}

并且该模块被导入到posts.ts中:

    ///<reference path="../../../typings/tsd.d.ts" />
    ///<reference path="../../../typings/typescriptApp.d.ts" />

    import {PostsService, PostsServiceImpl} from 'components/posts/posts.service';

    @Component({
    selector: 'posts',
    viewInjector: [
        //PostsServiceImpl
        //PostsService
        bind(PostsService).toClass(PostsServiceImpl)
    ]
})
...
export class Posts {
    private postsServices: PostsService;

    constructor(postsService: PostsService) {
        console.log('Loading the Posts component');
        this.postsServices = postsService;
        ...
    }

    ...
}

上面的代码编译正确,但基本上我的问题归结为 Angular 不会在 Posts 组件中注入 PostsServiceImpl 的实例。

当然这只是因为我没有找到正确的方法来声明/导出/导入所有内容

如果没有export interface PostsService ...,我会收到 TS 编译错误,因为我在帖子控制器中使用它。

没有export var PostsService:PostsServiceImpl;我在 @View 装饰器的 viewInjector 中得到 TS 编译错误

在生成的 js 代码中,我只找到exports.PostsService;由于导出变量而添加的...我知道接口在运行时会消失。

所以我对这一切有点迷茫。任何实用的想法如何使用 TypeScript 和 Angular 2 的基于接口的编程?

4

4 回答 4

6

任何实用的想法,我如何使用 TypeScript 和 Angular 2 使用基于接口的编程

接口在运行时被擦除。事实上,装饰器也不支持接口。所以你最好使用运行时确实存在的东西(即实现)。

于 2015-07-03T08:34:59.037 回答
4

你必须记住,你的类可能(并且应该)依赖于抽象,但是有一个地方你不能使用抽象:它在 Angular 的依赖注入中。

Angular 必须知道使用什么实现。

例子 :

/// <reference path="../../../../../_reference.ts" />

module MyModule.Services {
    "use strict";

    export class LocalStorageService implements ILocalStorageService {
        public set = ( key: string, value: any ) => {
            this.$window.localStorage[key] = value;
        };

        public get = ( key: string, defaultValue: any ) => {
            return this.$window.localStorage[key] || defaultValue;
        };

        public setObject = ( key: string, value: any ) => {
            this.$window.localStorage[key] = JSON.stringify( value );
        };

        public getObject = ( key: string ) => {
            if ( this.$window.localStorage[key] ) {
                return JSON.parse( this.$window.localStorage[key] );
            } else {
                return undefined;
            }
        };

        constructor(
            private $window: ng.IWindowService // here you depend to an abstraction ...
            ) { }
    }
    app.service( "localStorageService",
        ["$window", // ... but here you have to specify the implementation
            Services.LocalStorageService] );
}

因此,在您的测试中,您可以轻松地使用模拟,因为您的所有控制器/服务等都依赖于抽象。但对于实际应用,角度需要实现。

于 2015-07-03T09:11:17.057 回答
3

TypeScript 不会为接口生成任何符号,但您将需要某种符号来使依赖注入工作。

解决方案:使用 OpaqueTokens 作为符号,通过 provide() 函数为该标记分配一个类,并在类的构造函数中使用带有该标记的 Inject 函数。

在此处的示例中,我假设您希望将 PostsServiceImpl 分配给 Posts 组件中的 PostsService,因为当代码都在同一个位置时更容易解释。provide() 调用的结果也可以放入 angular.bootstrap(someComponent, arrayOfProviders) 的第二个参数,或者是比 Posts 组件更高层次的组件的 provide[] 数组。

在 components/posts/posts.service.ts 中:

import {OpaqueToken} from "@angular/core";
export let PostsServiceToken = new OpaqueToken("PostsService");

在您的构造函数中:

import {PostsService, PostsServiceImpl, PostsServiceToken} from 'components/posts/posts.service';
import {provide} from "@angular/core";
@Component({
    selector: 'posts',
    providers: [provide(PostsServiceToken, {useClass: PostsServiceImpl})]
})
export class Posts {
    private postsServices: PostsService;

    constructor(
        @Inject(PostsServiceToken)
        postsService: PostsService
        ) {
        console.log('Loading the Posts component');
        this.postsServices = postsService;
        ...
    }

    ...
}    
于 2016-08-11T03:22:30.583 回答
1

在打字稿中,您可以实现类。

例子

config.service.ts:

export class ConfigService {
  HUB_URL: string;
}

export class ConfigServiceImpl implements ConfigService {
  public HUB_URL = 'hub.domain.com';
}

export class ConfigServiceImpl2 implements ConfigService {
  public HUB_URL = 'hub2.domain.com';
}

app.component.ts:

import { Component } from '@angular/core';
import { ConfigService, ConfigServiceImpl, ConfigServiceImpl2 } from './config.service';

@Component({
  ...
  providers: [
    { provide: ConfigService, useClass: ConfigServiceImpl }
    //{ provide: ConfigService, useClass: ConfigServiceImpl2 }
  ]
})
export class AppComponent {
}

然后,您可以为服务提供不同的实现。

于 2017-01-03T02:47:16.220 回答