I am late to the party, but it seems one aspect is missing.
NodeJs doesn't execute your module code every time you use require. It is more like a kind of stateful container, that initializes your module once and passes this instance each time you use require.
I am nodejs noobie, so don't use following without discussion with someone more mature, but I adhere for software principles, that considers using static methods evil (e.g. it is better to construct interface contracts against interfaces, not against concrete interface implementation; you just don't simply make it with static methods).
In other languages, it is usual corner stone to have some IoC container, that has all of your modules registered and solves passing of dependencies for you. Then you write everything as "Service" classes. Service class is instantiated most often only once per application life-time and every another piece of code, that requires it gets the same instance from the IoC container.
So I use something similar, without the comfort of IoC :( :
Note in this example - A's constructor is called only once, althought required 3 times.
Test.ts:
import {a} from './A';
import {b} from './B';
import {c} from './C';
console.log(c, b);
A.ts:
export class A
{
constructor(){
console.log('"A" constructor called');
}
foo() {
console.log('foo');
}
}
export const a = new A();
B.ts:
import {a, A} from './A';
export class B
{
constructor(a: A)
{
console.log('"B" constructor called, got a:', a);
a.foo();
}
}
export const b = new B(a);
C.ts:
//The same as B.ts
Result:
node test.js
"A" constructor called
"B" constructor called, got a: A {}
foo
"C" constructor called, got a: A {}
foo
C {} B {}
So as You can see - no static methods. Works with instances (althought not with interfaces in this simplified example). A's constructor called only once.