2015-07-03 1 views
8

В настоящее время я пытаюсь очистить некоторый код, чтобы программировать против интерфейсов, а не от реализаций, но не могу понять, как это сделать.Программирование на основе интерфейса с использованием TypeScript, Angular 2 и SystemJS

Чтобы быть более конкретным, я использую: * машинопись 1.5.0 бета -> transpiled к 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 ..., я получаю ошибку компиляции TS, потому что я использую его в контроллере сообщений.

Без export var PostsService:PostsServiceImpl; я получаю ошибку компиляции TS в @View декоратора viewInjector

В коде сгенерированного JS я только найти exports.PostsService;, который добавляется из-за экспорт вара ... Я знаю, что интерфейсы исчезают во время выполнения.

Так что я немного потерял во всем этом. Любые практические идеи, как я могу использовать программирование на основе интерфейса w/TypeScript & Angular 2?

ответ

6

Любые практические идеи, как я могу использовать программирование на основе интерфейса ж/Машинопись & Угловое 2

Интерфейсы удаляются во время выполнения. На самом деле декораторы также не поддерживают интерфейсы. Таким образом, вам лучше использовать то, что существует во время выполнения (т. Е. Реализации).

+2

Это печальная новость для меня ;-). Наверное, я должен просто адаптироваться и забыть свой полностью типизированный мир Java. ^^ – dSebastien

+3

Как мне подойти к развязке в моем приложении? Я еще не там, но моя цель - написать тестовый код. Afaik еще нет поддержки абстрактных классов в TypeScript, правильно? – dSebastien

+1

Вскоре: https://github.com/Microsoft/TypeScript/pull/3579 – basarat

4

Вы должны иметь в виду, что ваш класс может (и должен) зависеть от абстракций, но есть одно место, где вы не можете использовать абстракцию: это интоксикацией зависимость Угловое.

Угловой должен знать, какую реализацию использовать.

Пример:

/// <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]); 
} 

Так что в вашем тесте вы можете использовать издевается легко, как все контроллеры/услуги и т.д. зависят от абстракций. Но для реального применения, угловые реализации потребностей.

+0

Ну, моя (наивная) идея заключалась в том, чтобы использовать 'bind (PostsService) .toClass (PostsServiceImpl)' именно для этого, позволяя Angular знать, как сопоставлять аннотация к конкретному. Это могло бы иметь смысл, если интерфейсы не были стерты во время выполнения, как указано @basarat. Что касается кода клиента, я хотел бы полностью скрыть тот факт, что была внедрена конкретная реализация. – dSebastien

3

TypeScript не создает никаких символов для интерфейсов, но вам понадобится какой-то символ, чтобы сделать работу по вложению зависимостей.

Решение: используйте OpaqueTokens в качестве символа, назначьте класс этому токену с помощью функции обеспечения() и используйте функцию Inject с этим токеном в конструкторе вашего класса.

В примере здесь я предположил, что вы хотите назначить PostsServiceImpl для PostsService в компоненте Posts, потому что это проще объяснить, когда код находится в одном месте. Результаты вызова функции() также могут быть помещены во второй аргумент angular.bootstrap (someComponent, arrayOfProviders) или массив обеспечения [] компонента выше иерархии, чем компонент Posts.

В компонентов/сообщений/posts.service.ts:

В конструкторе:

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; 
     ... 
    } 

    ... 
}  
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 { 
} 

Вы можете предоставить услугу с различными реализациями.

+0

Я использовал этот подход, и он довольно эффективен. – wickdninja

+0

Я, как правило, префикс базового класса с I (то есть IConfigService), хотя это не фактический интерфейс. Это соглашение об именах, по крайней мере, передает мои намерения для этого класса. – wickdninja

Смежные вопросы