Interface-based programming with TypeScript, Angular 2, and SystemJS

I'm currently trying to clear some code to program against interfaces, not implementations, but I can't figure out how to do this.

To be more specific, I use: * TypeScript 1.5.0 beta -> passed to ES5 / commonjs * SystemJS to load modules

I am currently trying to use external modules as follows:

posts.service.ts file:

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

and this module is imported into 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; ... } ... } 

The above code compiles correctly, but basically my problem is that Angular will not insert an instance of PostsServiceImpl into the Posts component.

Of course, this is simply because I did not find the right way to declare / export / import everything

Without the export interface PostsService ... , I get TS compilation errors because I use it in a message controller.

Without export var PostsService:PostsServiceImpl; I get TS compilation errors in @View decorInjector constructor view

In the generated js code, I find only exports.PostsService; , which is added due to var export ... I know that the interfaces disappear at runtime.

So I'm a little lost in all of this. Any practical ideas on how I can use w / TypeScript and Angular 2 based programming?

+8
angular typescript systemjs
source share
4 answers

Any practical ideas how I can use w / TypeScript and Angular 2 based programming

Interfaces are erased at runtime. In fact, decorators also do not support interfaces. Therefore, you are better off using something that exists at runtime (i.e., Implementations).

+6
source share

You should keep in mind that your class can (and should) depend on abstractions , but there is one place where you cannot use abstraction: it is in Angular dependency injection,

Angular needs to know which implementation to use.

Example:

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

Thus, in your test, you can easily use mocks, since all your controllers / services, etc. depend on abstractions. But for a real angular application, implementations are needed.

+4
source share

TypeScript does not create characters for interfaces, but you will need some kind of character to do the work of nesting dependencies.

Solution: use OpaqueTokens as your character, assign the class to this token using the offer () function, and use the Inject function with this token in the constructor of your class.

In this example, I assumed that you want to assign PostsServiceImpl to PostsService in the Posts component, because it is easier to explain when the code is in one place. The results of calling the function () can also be placed in the second argument to angular.bootstrap (someComponent, arrayOfProviders) or the array of the [] component of the hierarchy higher than the Posts component.

In the components /posts/posts.service.ts:

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

In your constructor:

 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; ... } ... } 
+3
source share

In typescript you can implement classes.

Example

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

Then you can provide a service with various implementations.

+1
source share

All Articles