I'd like to be able to inject a service with a generic constrained to an interface in Angular, using a common factory... I can do it if I declare a provider of every injected type, but that defeats the purpose of why I want it this way.
What I want is something like:
interface WorkerServiceContract {
doWork(): void;
}
class MyService<T extends WorkerServiceContract> {
constructor(private worker: T) {}
doWorkWrapper() { this.worker.doWork(); }
}
So at any point I can do a:
@Injectable({ providedIn: 'root' })
class FooWorkerService implements WorkerServiceContract {
doWork() { console.log('foo'); }
}
@Injectable({ providedIn: 'root' })
class BarWorkerService implements WorkerServiceContract {
doWork() { console.log('bar'); }
}
@Component(/*blabla*/)
class MyComponent {
constructor(private fooWorker: MyService<FooWorkerService>, private barWorker: MyService<BarWorkerService>) {}
}
I understand I can declare the injection using specific tokens for each of the WorkerServiceContract possibilities, but I'm wondering if there's a way (I've looked through the documentation but couldn't figure it out), to have it "open"... so I could do something like (this wouldn't obviously work):
providers: [
{
provide: MyService<T extends ServiceWorker>
useFactory: (worker: T) => new MyService<T>(worker);
deps: [T]
}
]
I understand that's not possible in the provider definition (since it doesn't know T), but is there any mechanism which would allow something "generic" like this to work? It's probably something obvious but I can seem to get my head around to do it
I'm using Angular 9
Real-Life scenario (addendum)
The whole rationale about why we want this (the real-life scenario) is:
I have a tool-generated service class (from Swagger/OpenApi). For that service, I create a proxy/wrapper service that intercepts all the http (not http, but the methods, which do more than just calling the http client) calls to the API and pipes the returned observables to handle errors (and successes, actually) to show UI notifications (and create other calls to diagnostic logging servers, etc.).
These handlers are sometimes are generic, but each view (depending on the called API) may want to have their own handlers (e.g., one may show a toast, one may show a popup, one might want to transform the call to the API in some ways, or whatever).
I could do it on every call to the API, but on my team, we've found out that separating those concerns (the handling of a successful call with normal data received from the API, and the handling of errors) helps both the readability and size of the code of the components (and the responsibility of each code). We've already solved that by a "simple" call on the constructor, e.g.:
constructor(private apiService: MyApiService) {
this.apiService = apiService.getProxyService<MyErrorHandler>();
}
Which returns a Proxy that handles all that. This works just fine, but we were discussing the idea of making it "even cleaner", like so:
constructor(private apiService: MyApiService<MyErrorHandler>) {}
And have a factory on the DI container create that proxy for us, which would bring the benefit of both: a) not having to remember doing that call on the constructor, and b) have a clear view of ALL the dependencies (including the error handler) directly on the constructor parameters (without having to dig into the constructor code, which could have other stuff depending on the actual component)
And no, an HttpClient interceptor wouldn't work here, since the auto-generated service does more than the HttpClient call (and we want to work on what gets returned from that service, not directly on the HttpResponse object)