NodeJS globals and TypeScript
About NodeJS globals
2022-02-14 - Updated for @types/node@16
NodeJS allows you to set process-level global values that can be accessed in any module.
In general I would consider implicitly sharing global values between modules a bad practice, however in certain situations it can be a pragmatic choice.
Assigning globals using pure JavaScript is straightforward, making them work with TypeScript is another thing. Let’s dive in.
NodeJS globals in JavaScript
Setting a global value in NodeJS couldn’t be simpler:
1 | global.someValue = 'My hovercraft is full of eels'; |
NodeJS globals in TypeScript
Note: Make sure that you have @types/node
installed.
Let’s start by trying to just assign the value and see what happens:
1 | global.someValue = 'My hovercraft is full of eels'; |
If we look at the type of global
it’s globalThis
. Fortunately TypeScripts declaration merging allows us to extend this interface to include our new attribute:
1 | declare global { |
This declaration can be placed in any .ts
file in your project. Just make sure that it is imported and the global value is assigned before any other module would use it.
Note: For @types/node < 16 you need to go with:
1
2
3
4
5
6
7
8 declare global {
var someValue: string;
namespace NodeJS {
interface Global {
someValue: string;
}
}
}
Actual real life use cases
Making winston logger globally available:
1 | // logging.ts |
Making ramda globally accessible:
1 | import * as ramda from 'ramda'; |
Final thoughts
You may wonder if it makes sense to use an implicit global instead of just importing the value explicitly.
I don’t think there is one answer to that. For example, in my case, using the implicit log
from example above felt smoother than having to import it every time when I wanted to log something, even with IDE automated imports. I feel that this made me use log
more often which led to slightly better quality of application logs. It can also remove the headache of constantly adding and removing the import statement if it turns out that the last log
was removed and linter throws errors at you that it is no longer needed. Also having the globals explicitly typed, with clear lifecycle doesn’t make them look like a hack.
Just keep in mind that abusing global
can lead to a codebase that is both hard to read and reason about.