Search code examples
node.jstypescriptdependency-injectiontsyringe

Dynamic Dependency injection with Typescript using tsyringe


I am trying to build and example to understand how the DI framework/library works, but i am encountering some problems.

I have this interface with two possible implementations:

export interface Operation {
    calculate(a: number, b: number): number;
}

sub.ts

import { Operation } from "./operation.interface";

export class Sub implements Operation {
    calculate(a: number, b: number): number {
        return Math.abs(a - b);
    }
}

sum.ts

import { Operation } from "./operation.interface";

export class Sum implements Operation {
    calculate(a: number, b: number): number {
        return a + b;
    }

}

calculator.ts

import { Operation } from "./operation.interface";
import {injectable, inject} from "tsyringe";

@injectable()
export class Calculator {
    constructor(@inject("Operation") private operation?: Operation){}

    operate(a: number, b: number): number {
        return this.operation.calculate(a, b);
    }
}

index.ts

import "reflect-metadata";
import { container } from "tsyringe";
import { Calculator } from "./classes/calculator";
import { Sub } from "./classes/sub";
import { Sum } from "./classes/sum";

container.register("Operation", {
    useClass: Sum
});

container.register("OperationSub", {
    useClass: Sub
});

const calculatorSum = container.resolve(Calculator);

const result = calculatorSum.operate(4,6);
console.log(result);

// const calculatorSub = ???

is there a way where I could have two calculators with different behaviours or am I doing it completely wrong?


Solution

  • Since OperationSub isn’t used anywhere, it cannot affect injected Operation value.

    Calculators with different dependency sets should be represented with multiple containers. Summing calculator can be considered a default implementation and use root container, or both implementations can be represented by children containers while root container remains abstract.

    // common deps are registered on `container`
    const sumContainer = container.createChildContainer();
    const subContainer = container.createChildContainer();
    
    sumContainer.register("Operation", { useClass: Sum });  
    subContainer.register("Operation", { useClass: Sub });
    
    const calculatorSum = sumContainer.resolve(Calculator);
    const calculatorSub = subContainer.resolve(Calculator);