Say we have a class structure like the following:// implementation.tsexport class A implements IA {    constructor(private b: IB) {}}export class B implements IB {    constructor(private c: IC) {}}export class C implements IC {}// ... interface declarationsUsing an IoC container, it is possible to inject dependencies using annotations like @Inject or @AutoWired, however I believe there are two caveats:Using annotations adds further concerns to my classes. A, B and C should only be responsible for the implementation and not for what should be implemented in which manner.We risk to encounter runtime errors because the IoC container does not support compile-time checks whether each dependency is met.So I thought a bit and came up with an additional layer responsible for dependency injection:// dependency-injection.tslet impl = require(./implementation);export class A extends impl.A {    constructor() {        super(new B());}export class B extends impl.B {    constructor() {        super(new B());}export class C extends impl.C {}To instatiate A with the injected dependencies in my application on startup, I would simply call new diModule.A().This solution has many advantages:does not require any frameworksupports compile-time checks whether a dependency is metseparates the concerns of implementation and wiringis very flexible, i.e. does not constrain the way of wiring...while keeping the advantages of an IoC container which come to my mind:allows sophisticated dependency injectionavoids constructor nesting: new A(new B(new C()))when updating a dependency, I have to change one and only one classit is quite easy to apply different implementations for different purposes, for example by having two classes LoggerForWarnings and LoggerForErrors.Which of these two aspect-oriented approaches would you prefer and why? Is there a name for my approach? Are there further disadvantages of the DI layer or further advantages of an IoC container?Having decided which answer to accept, I'll try to summarize some aspects of the two answers. I discussed a lot with Adrian Iftode and although I'm still critical about IoC containers without compile-time dependency checks, his arguments were worth a +1 - but unfortunately, I'm missing the reputation.Christian Willman noted that for projects which don't exceed a certain size and complexity, my approach is often realized without all the class foo, simply wiring all the classes by constructor injection at composition root directly.Adrian Iftode remarked that IoC containers can do much more than the classes A, B and C can - for example, they can manage the lifetime of objects or provide singletons. This functionality could be added to my example by using factories instead of constructors in the DI layer.What I'm still wondering about is if the approach is also useful for larger projects. In my opinion, directly doing constructor injection in main() will be quite difficult to maintain while creating several factories in the DI layer could improve maintainability whilst keeping the advantages of other IoC containers.Thank you two very much!
IoC Framework vs. DI Layer
dependency injection;single responsibility;ioc;ioc containers;typescript
You can actually get away with a much simpler implementation. Just manually wire everything together in the so-called composition root, which is just the application's entrypoint.Of course you need to be mindful of the dependency resolution order and first instantiate the classes which themselves have no dependencies (that is, the sinks in the object dependency graph).In your example: function main() {  const c: C = new C();  const b: B = new B(c);  const a: A = new A(b);}The benefits are twofold. You don't clutter your classes with default constructors, and the (brutally) straightforward instantiation process makes the code flow obvious to maintainers.