typescript - Angular 2 injicerbar gränssnitt?


Idag snubblade jag över något som jag inte trodde skulle orsaka mig problem.

I Java och Spring kan jag deklarera två beans som båda implementerar ett givet gränssnitt, medan jag i en annan klass där de injiceras bara arbetar med gränssnittet; detta är faktiskt vad jag älskar med IoC: du behöver egentligen inte veta vilket objekt du arbetar med, bara dess typ.

Så i mitt lilla Angular2/Typescript-program försökte jag göra samma sak:

webapp.module.ts:

... 
import { WebAppConfigurationService } from './app/services/webapp.configuration.service';

@NgModule({
  ...
  providers: [WebAppConfigurationService]
})
export class AppModule { }

tnsapp.module.ts:

...
import { TnsConfigurationService } from './services/tns.configuration.service';

@NgModule({
   ...
   providers: [TnsConfigurationService]
})
export class AppModule { }

Båda dessa moduler använder en annan leverantör: TnsConfigurationService eller WebAppConfigurationService.

Dessa två @Injectable-tjänster implementerar dock samma gränssnitt:

konfiguration.gränssnitt:

export interface IConfigurationService {
    ...
}

Slutligen, i en av mina komponenter använder jag den injicerbara som tillhandahålls av en av dessa moduler jag visade dig i början:

import { IConfigurationService } from './configuration.interface';

export class HeroesService {

    constructor(private configurationService: IConfigurationService) { }
}

Min förväntning var att denna sista komponent skulle injiceras med rätt tjänst, även om parametern endast uttryckligen definierar gränssnittet. Självklart får jag ett fel ("Fel: Kan inte lösa alla parametrar för HeroesService")

Nu förväntar jag mig inte en enkel lösning på detta eftersom det låter som en arkitektonisk brist. Men kanske kan någon peka mig mot en alternativ design?

Svar #1

För att en leverantör ska kunna injiceras, bör den registreras som en leverantör. Det finns ingen IConfigurationService-leverantör. Och den kan inte vara en leverantör, eftersom gränssnitt inte finns i kompilerad JS-kod.

Den vanliga metoden för gränssnitt som ska användas som leverantörstoken är att vara abstrakta klasser:

abstract class ConfigurationService { ... }

@Injectable()
class WebAppConfigurationService extends ConfigurationService { ... }

...
providers: [{ provide: ConfigurationService, useClass: WebAppConfigurationService }]
...

Detta recept används ofta av Angular 2 själv, t.ex. den abstrakta NgLocalization klassen och den konkreta NgLocaleLocalization implementationen.

Relaterade artiklar