1

I'm working on a backend solution in Node.js and Express, using Typescript. I'm trying to do dependency injection similar to Angular, but lacking the @Injectable() decorator I'm doing this:

Dependency:

export class SomeDependency {
  public someMethod() {
    console.log('Some method is running');
  }
}

Parent:

import { SomeDependency } from './someDependency';

export class Whatever {

  constructor(
    private someDependency = new SomeDependency()
  ) {}

  public doSomething() {
    console.log('Look, I\'m doing something!');
    this.someDependency.someMethod();
  }
}

It works, but it may not be the best way. Any suggestions on how to improve it are appreciated.

On the other hand, what do you guys think: is it better to import dependencies like this, in the constructor, or create new instance every time, or most of the time? Like this:

import { SomeDependency } from './someDependency';

export class Whatever {

  public doSomething() {
    console.log('Look, I\'m doing something!');
    new someDependency().someMethod();
  }
}

As SomeDependency isn't a singleton, I wonder which one is less efficient: keeping an instance alive in the parent, or creating a new one every time, letting the garbage collector take care of it when the call finished.

Tamás Polgár
  • 2,082
  • 5
  • 21
  • 48
  • IMO, I'd suggest only using a `class` when you need to bundle data together with methods that operate on that data. If you have only methods, a plain function (or object of functions) would make more sense. If you have only data, a plain object or array would make more sense. For this example here, I'd do `import { someMethod } from './someMethod';` where `someMethod` is the function - then just call the function whenever needed. – CertainPerformance Aug 23 '20 at 21:08
  • 1
    I think a lot of the confusion and overuse of classes may come from those with OO backgrounds like Java - but JS is quite different – CertainPerformance Aug 23 '20 at 21:11
  • Thanks, but this is just a simple example of a more complex thing I'm doing. The dependency is usually a controller or Express middleware with dozens of methods. So importing just a single function won't do. – Tamás Polgár Aug 23 '20 at 22:28
  • 1
    Hi, do you know [tsyringe](https://github.com/microsoft/tsyringe) or [typedi](https://github.com/typestack/typedi) or [inversifyJs](https://github.com/inversify/InversifyJS) for dependency injection tools? Also this post [Dependency injection stackoverflow](https://stackoverflow.com/questions/9250851/do-i-need-dependency-injection-in-nodejs-or-how-to-deal-with) may help you :-) – gonzalo Dec 11 '20 at 10:43

2 Answers2

1

your use case is absolutely correct. It is always a good practice to keep your modules as lightly coupled. So, in future you can switch between the implementation without touching the main logic but the Javascript or Typescript doesn't provide the dependency injection mechanism.

To achieve this you can use a library know as Inversify.

Inversify is powerful and lightweight inversion of control container for JavaScript & NodeJS. It implements IoC and allow us to inject dependencies, uncoupling our method from any implementation details

It give you the same functionality as we use in Angular like you can annotate the class with @Injectable() annotation and perform dependency injection.

For reference you can go to: https://www.linkedin.com/pulse/how-use-dependency-injection-nodejs-typescript-projects-ribeiro

1

You may want to look into Dime. It's a very simple library I made for dependency injection similar to Angular. There are more details on the Github page and the wiki. It's still in early development, so there might be bugs.

Example:

import { ItemsService } from './items-service'; // ItemsService is an interface
import { Inject } from '@coined/dime';

class ItemsWidget {
    @Inject()
    private itemsService: ItemsService;

    render() {
        this.itemsService.getItems().subscribe(items => {
            // ...
        })
    }
}

// Setup
const appPackage = new Package("App", {
    token: "itemsService",
    provideClass: AmazonItemsService // Use any implementation
});

Dime.mountPackages(appPackage);

// Run the application
const widget = new ItemsWidget();
widget.render();
anut
  • 481
  • 1
  • 11