私は現在、実装ではなくインターフェイスに対してプログラムするために、いくつかのコードをクリーンアップしようとしていますが、その方法を理解できません。
より具体的には、私は以下を使用しています:* TypeScript 1.5.0ベータ版-> ES5/commonjsにトランスパイル*モジュールをロードするSystemJS
私は現在、次のように外部モジュールを使用しようとしています:
posts.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はPostsServiceImplのインスタンスをPostsコンポーネントに挿入しないという事実が原因です。
もちろん、それはすべてを宣言/エクスポート/インポートする正しい方法が見つからないからです
export interface PostsService ...
がないと、postsコントローラーで使用するため、TSコンパイルエラーが発生します。
export var PostsService:PostsServiceImpl;
がないと、@ ViewデコレータのviewInjectorでTSコンパイルエラーが発生します
生成されたjsコードでは、エクスポート変数のために追加されたexports.PostsService;
のみが見つかります...実行時にインターフェイスが消えることを知っています。
だから私はこのすべてで少し迷っています。 TypeScript&Angular 2?
TypeScript&Angular 2を使用したインターフェイスベースのプログラミングの使用方法に関する実用的なアイデア
インターフェースは実行時に消去されます。実際、デコレータはインターフェースもサポートしていません。したがって、実行時に存在するもの(つまり、実装)を使用する方がよいでしょう。
あなたのクラスは(そしてすべきである)は抽象化に依存するであることを覚えておく必要がありますが、抽象化は使用できません。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] );
}
したがって、すべてのコントローラー/サービスなどが抽象化に依存しているため、テストではモックを簡単に使用できます。しかし実際のアプリケーションでは、angularは実装が必要です。
TypeScriptはインターフェイスのシンボルを生成しませんが、依存関係注入を機能させるには、何らかのシンボルが必要になります。
解決策:シンボルとしてOpaqueTokensを使用し、provide()関数を介してそのトークンにクラスを割り当て、クラスのコンストラクターでそのトークンを使用してInject関数を使用します。
この例では、PostsServiceImplをPostsコンポーネントのPostsServiceに割り当てることを想定しています。これは、コードがすべて同じ場所にある場合を説明する方が簡単だからです。 prepare()呼び出しの結果は、angular.bootstrap(someComponent、arrayOfProviders)の2番目の引数、またはPostsコンポーネントよりも上位のコンポーネントのprepare []配列にも入れることができます。
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;
...
}
...
}
TypeScriptでは、クラスを実装できます。
例
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 {
}
その後、さまざまな実装でサービスを提供できます。