I recently started using tsyringe
and I came across an issue with how dependencies are resolved. A reproducible scenario is explained below:
class Client {
exec(svcNum: number) {
console.log("Executing ", svcNum);
}
}
@injectable()
class Service1 {
private readonly num = 1;
constructor(private client: Client) {}
serve() {
this.client.exec(this.num);
}
}
@injectable()
class Service2 {
private readonly num = 2;
constructor(private client: Client) {}
serve() {
this.client.exec(this.num);
}
}
I have a Client
class that is a dependency used by Service1
and Service2
. These can be easily resolved like this:
const resolvedSvc1 = container.resolve(Service1);
const resolvedSvc2 = container.resolve(Service2);
Now what I need is something like this:
container.register(Service1, { useClass: Service1 });
container.register(Service2, { useClass: Service2 });
const services = [ Service1, Service2 ]; // typeof Service1 | Service 2
services.forEach((svc) => {
//container.register((svc as any).name, { useClass: svc });
if (container.isRegistered((svc as any).name)) {
console.log("Found registered service ", svc.name);
}
const resolved = container.resolve<typeof svc>(svc.name);
(resolved as any).serve();
});
This does not work and TypeScript complains that Error: Attempted to resolve unregistered dependency token: "Service1"
when I run the code; also, the console.log
prints nothing in the if
block. I have found that svc
has a union type Service1 | Service2
and this probably causes the issue. Is there any way I can resolve the dependencies for services dynamically in this way at runtime? Narrowing svc
to the appropriate type (i.e., either Service1
or Service2
will work I guess).
CodePudding user response:
You are registering your services using the class
but then trying to retrieve them by name
:
container.register(Service1, { useClass: Service1 });
// ^^^^^^^^ ✅ registering the class here
const resolved = container.resolve<typeof svc>(svc.name);
// ❌ trying to resolve by name ^^^^^^^^
There are two solutions for this:
// Option 1: register by class and resolve by class
container.register(Service1, { useClass: Service1 });
// ^^^^^^^^ ✅ register by class
const resolved = container.resolve<typeof svc>(svc);
// ✅ resolve by class ^^^
// Option 2: register using the class name and resolve by class or class name
container.register(Service1.name, { useClass: Service1 });
// ^^^^^^^^^^^^^ ✅ register by class name
// can resolve by class
const resolved = container.resolve<typeof svc>(svc);
// ✅ resolve by class ^^^
// can resolve by class name
const resolved = container.resolve<typeof svc>(svc.name);
// ✅ resolve by class name ^^^^