typescriptinversion-of-controltsyringe

How to register different implementations of an interface in a tsyringe container based on a given parameter


I am trying to find an elegant way to inject an Interface into the tsyringe container based on a given value. (string/enum/Symbol)

This is how I would do it with a switch..case clause:

interface IColor{
  someMethod():void;
  readonly someInfo:any;
}

class Blue implements IColor{
  constructor() {
  }
  someMethod() {
    console.log('I am blue');
  }
}
class Red implements IColor{
  constructor() {
  }
  someMethod() {
    console.log('I am red');
  }
}
@injectable()
class SurfaceService{
  constructor(@inject('IColor')readonly color:IColor) {
  }
}

// main
import {container} from "tsyringe";
const mainFunction = (someData:{colorinfo:string}) =>{

  switch (someData.colorinfo){
    case 'RED':
      container.register("IColor", {useClass: Red});
      break;

    case 'Blue':
      container.register("IColor", {useClass: Blue});
      break;
  }

  const service = container.resolve(SurfaceService); 
}

The problem with this approach is, that it is not very elegant and it requires to import all possible implementation even though, only one is needed at runtime.

Is there a better solution for this?


Solution

  • Here is a gist which was part of my research of DI solutions.

    Apparently one has to use predicateawareclassfactory. But I've found that a simpler version, that worked just fine ¯\_(ツ)_/¯

    container.register(Tokens.logger, {
      useFactory: () => {
        return process.env.NODE_ENV === "production"
          ? container.resolve(PinoLogger)
          : container.resolve(ConsoleLogger)
      },
    })