Extending TypeScript Global object in node.js

node.jsTypescriptGlobal

node.js Problem Overview


I have a node.js app that attaches some config information to the global object:

global.myConfig = {
    a: 1,
    b: 2
}

The TypeScript compiler doesn't like this because the Global type has no object named myConfig:

> TS2339: Property 'myConfig' does not exist on type 'Global'.

I don't want to do this:

global['myConfig'] = { ... }

How do I either extend the Global type to contain myConfig or just tell TypeScript to shut up and trust me? I'd prefer the first one.

I don't want to change the declarations inside node.d.ts. I saw this SO post and tried this:

declare module NodeJS  {
    interface Global {
        myConfig: any
    }
}

as a way to extend the existing Global interface, but it doesn't seem to have any effect.

node.js Solutions


Solution 1 - node.js

> I saw this SO post and tried this:

You probably have something like vendor.d.ts:

// some import 
// AND/OR some export

declare module NodeJS  {
    interface Global {
        spotConfig: any
    }
}

Your file needs to be clean of any root level import or exports. That would turn the file into a module and disconnect it from the global type declaration namespace.

More : https://basarat.gitbooks.io/typescript/content/docs/project/modules.html

Solution 2 - node.js

To avoid Typescript claim something like this:

>TS2339: Property 'myConfig' does not exist on type 'Global'.

I suggest to define custom types. I do it under src/types/custom.d.ts file in my project:

declare global {
  namespace NodeJS {
    interface Global {
        myConfig: {
          a: number;
          b: number;
        }
    }
  }
}

Then I ensure these are considered by Typescript in tsconfig.json file:

{
  ...
  "files": [
    ...
    "src/types/custom.d.ts"
  ]
}

Now you're safe to use your custom property:

console.log(global.myConfig.a);

Solution 3 - node.js

As of node@16 the NodeJS.Global interface has been removed in favor of globalThis.

You can declare new global variable in a module file as:

declare global {
  var NEW_GLOBAL: string;
}

And in a non-module file (no top-level import/export) as:

declare var NEW_GLOBAL: string;

Important note: variable must be declared as var. let or const variables doesn't show up on globalThis.

Solution 4 - node.js

Putting the following file into our project's root directory worked.

global.d.ts

declare namespace NodeJS {
  export interface Global {
    myConfig: any
  }
}

We're using "@types/node": "^7.0.18" and TypeScript Version 2.3.4. Our tsconfig.json file looks like this:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6"  
    },
    "exclude": [
        "node_modules"
    ]
}

Solution 5 - node.js

Use 'namespace' instead of 'module' to declare custom TypeScript

If you use any of the above answers and are using a newer version of Typescript you'll get a nag about using "module". You should consider namespace instead.

In order to satisfy the requirement here you'll actually need more than extending the Global interface. You'll also need to create a constant with the type as well if you want it to be accessible directly from the "globalThis" context.

NOTE: while the OP asked about an object literal the process is the same as you see here below. Rather than the "Debug" type being a function you would simply define the interface as need, then change "debug:" to myConfig or whatever you wish.

// typically I'll store the below in something like "typings.d.ts"
// this is because, at least typically, these overrides tend to
// be minimal in nature. You could break them up and Typescript
// will pick them up if you wish.

// Augmentations for the global scope can only be directly nested 
// in external modules or ambient module declarations.
export {}

declare global {
  // Definition or type for the function.
  type Debug = (label: string) => (message: any, ...args: any[]) => void

  // If defining an object you might do something like this
  // interface IConfig { a: number, b: number }

  // Extend the Global interface for the NodeJS namespace.
  namespace NodeJS {
    interface Global {
      // Reference our above type, 
      // this allows global.debug to be used anywhere in our code.
      debug: Debug
    }
  }
  
  // This allows us to simply call debug('some_label')('some debug message')
  // from anywhere in our code.
  const debug: Debug
}

How the above might be used

For completeness in this example all we did was define a global so we can log a simple debug message. Here's how we'd bind the method to our global context.

global.debug = (label: string) => (message: any, ...args: any[]) => console.log(message, ...args)

We can also call our global debug method directly:

debug('express')(`${req.method}: ${req.url}`)

Solution 6 - node.js

The only thing that works for me is this:

// lib/my.d.ts

import Global = NodeJS.Global;
export interface CDTGlobal extends Global {
  cdtProjectRoot: string
}

and then using it in other files like so

import {CDTGlobal} from "../lib/my.d.ts";
declare const global: CDTGlobal;

const cwd = global.cdtProjectRoot; // works

Solution 7 - node.js

What worked for me is:

declare global {
  module NodeJS {
    interface Global {
      myConfig: any;
    }
  }
}

global.myConfig = 'it works!';

Only downside is when using it you will have to turn off the ESLint rule @typescript-eslint/no-namespace.

For completeness here is my tsconfig.json:

{
  "compilerOptions": {
    "declaration": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "forceConsistentCasingInFileNames": true,
    "jsx": "react",
    "lib": ["dom", "es2017"],
    "module": "commonjs",
    "moduleResolution": "node",
    "noEmitOnError": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "outDir": "dist",
    "removeComments": true,
    "resolveJsonModule": true,
    "rootDir": "src",
    "sourceMap": true,
    "strict": true,
    "target": "es6"
  },
  "exclude": ["dist", "node_modules"]
}

Solution 8 - node.js

> Copy my answer of another post:

globalThis is the future.

// Way 1
var abc: number
globalThis.abc = 200 // no error

// Way2
declare var age: number
globalThis.age = 18 // no error

Solution 9 - node.js

I can get both type check and code intelligence.

declare namespace NodeJS {
  interface Global {
    property: string
  }
}

But interface Global is point to global.GLOBAL.

You can get correct type check, is because:

declare var global: NodeJS.Global & typeof globalThis;

But you can not get better code intelligence by useing global.property, unless use global.GLOBAL.property.

So, you need to define global var global and extend interface Global both:

// myglobal.d.ts
declare namespace NodeJS {
  interface Global {
    property: string
  }
}
declare var global: NodeJS.Global & typeof globalThis

And now you can get property intelligence when you type global.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiond512View Question on Stackoverflow
Solution 1 - node.jsbasaratView Answer on Stackoverflow
Solution 2 - node.jsNik SumeikoView Answer on Stackoverflow
Solution 3 - node.jsaleksxorView Answer on Stackoverflow
Solution 4 - node.jsShaun LuttinView Answer on Stackoverflow
Solution 5 - node.jsBlujedisView Answer on Stackoverflow
Solution 6 - node.jsAlexander MillsView Answer on Stackoverflow
Solution 7 - node.jsBenny NeugebauerView Answer on Stackoverflow
Solution 8 - node.jsedvard chenView Answer on Stackoverflow
Solution 9 - node.jsaolyangView Answer on Stackoverflow