Home > Back-end >  Type Resolution in tsyringe
Type Resolution in tsyringe

Time:09-08

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 ^^^^
  • Related