I am building an application with Angular (6.0.7
) and I am trying to create a service with the new:
@Injectable({
providedIn: 'root'
})
But how can I type an injection with an Interface?
The problem
I have 2 services, Authentication.service and SessionStorage.service. I want to inject the sessionstorage into the authentication service. That can be done via:
constructor(private sessionStorage: SessionStorage) {
}
No problem there. But for Object Orientated purposes I want to have an interface
above this service (so that I can implement both localstorage service as sessionstorage service). Thus it is only logical that I want to type the injected class with the interface, but this cannot be done the same way Angular 5 and lower does it.
So how can I type the injection into this global service with my interface?
I've tried
The Angular service typings describe an InjectableProvider
, but this does not match any of the parameters of the siblings of InjectableProvider
, so this gives a compiler (and tslint) error.
@Injectable({
providedIn: 'root'
}, {provide: IStorageService, useClass: SessionStorage})
This can be done with InjectionToken
, which is a replacement for the obsolete OpaqueToken
export const AuthenticationProvider = new InjectionToken(
"AuthenticationProvider",
{ providedIn: "root", factory: () => new CognitoAuthenticationProvider() }
);
...
@Injectable()
export class CognitoAuthenticationProvider implements IAuthenticationProvider {
...
@Injectable({
providedIn: "root"
})
export class AuthenticationService {
constructor(
@Inject(AuthenticationProvider)
private authenticationProvider: IAuthenticationProvider,
private http: HttpClient
) {}
I used something like the following to solve this
app.module.ts
providers: [
{ provide: AlmostInterface, useClass: environment.concrete }
...
]
AlmostInterface.ts
export abstract class AlmostInterface {
abstract myMethod();
}
MyConcrete.ts
export class MyConcrete implements AlmostInterface {
myMethod() { ... }; // implementation
}
export class MyConcreteAlternative implements AlmostInterface {
myMethod() { ... }; // implementation
}
environment.ts
export const environment = {
production: false,
concrete: MyConcreteAlternative
};
environment.prod.ts
export const environment = {
production: true,
concrete: MyConcrete
};
I think you can't use typescript interfaces for dependency injection as typescript interfaces don't exist at runtime (only for typesafety at compile time).
I would suggest using an abstract class for it.
EDIT:
It seems you can use useClass
in the first parameter of @Injectable, not as a second like your example. Combining that with @k0zakinio's answer results in:
@Injectable({
providedIn: 'root',
useClass: environment.concrete,
deps: []
})
export abstract class SessionStorage { }
It also seems you need to declare your dependencies via deps
or inject
, checkout this github issue. I hope this time my answer is of more help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With