Minduca Minduca - 4 months ago 26
TypeScript Question

How to inject a module into a class using Typescript and NodeJS

I'm developing in nodeJS + Typescript. I have a OO background and I want to benefit from nodejs modules, but I'm struggling to mix them with classes that are not supposed to be modules.

This is what I'm trying to do:

foo.ts (module)

import http = require("http")

export class Foo {
public fooMethod() : number { ... }
}


bar.ts (not supposed to be a module)

namespace Ns {
export class Bar {
constructor(private foo: Foo) { ... } //"Foo" is not being found by the intellisense
public barMethod() : number {
return this.foo.fooMethod()
}
}
}


server.js (node startup file)

var Foo = import("./foo");

var foo = new Foo();
foo.configure(...) //Configure foo before inject it into bar

var bar = new Ns.Bar(foo)





Issues I'm facing when Trying to structure the code like this :


  1. Bar can't see Foo. I tried to add a reference to the file, but it didn't work.

  2. It "worked" when I imported ./foo, but when I do that Bar can't see other exported types in other files that are defined on the same namespace (even if I write the full name of the type, i.e. include the namespace, it still can't see it).

  3. So I removed the namespace Ns in Bar and I could see other types when I typed its name with the namespace. But now Bar is a module and I It feels like my constructor injection smells, since Foo is imported and I can instantiate it directly.






I don't want to force my standards. I want to know what is the right approach for what I am trying to do. The struggle makes me feel that I'm obligated to redesign and go full modules when developing nodejs applications. Is that right?

In case that I should go full modules, how should I manage dependency injection?

Thank you

Answer

To fully leverage power of OOP (or better to say Interface-based programming or Protocol-oriented programming) you should use interface Foo to hide using of specific implementation MyFoo by Bar class.

Foo.ts

export interface Foo {
  fooMethod(): number;
}

MyFoo.ts

export class MyFoo {
  fooMethod(): number {
    return 1;
  }
}

Bar.ts

import {Foo} from './Foo'

export class Bar {
  constructor(private foo: Foo) {}

  barMethod(): number {
    return this.foo.fooMethod();
  }
}

Somewhere else:

import {Boo} from './Boo'
import {MyFoo} from './MyFoo'

const result = new Boo(new MyFoo()).barMethod();

Personally I do not recommend to use namespaces. You can read more about namespaces and modules here.