111

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.

11 Answers 11

155
+100

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 and const variables don't show up on globalThis.

Sign up to request clarification or add additional context in comments.

9 Comments

This is the only answer that works as of Sep 2021! (Node v14 LTS)
That's a very good point regarding var in the context of TS declarations.
Not working for me, the compiler still complains with the following message: 'Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature'.
and how to use this NEW_GLOBAL which is declared and set in Server.ts in eg. UserController? Do I need to somehow import it? How Typescript knows that it was already declared in Servert.s?
I'm not sure how to do it with Node18, I ended up using @ts-ignore, this stuff is unnecessarily complicated.
|
96

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

13 Comments

I can't quite figure this out. I don't have a vendor.d.ts, so I made one, but it seems to have no effect. Should I be importing it?
NodeJS is declared as a namespace, not as a module
@basarat - RE: "Your file needs to be clean of any root level import or exports" would it not be possible to apply the "global modifying module" here as per typescriptlang.org/docs/handbook/declaration-files/templates/… ? If I understand that example correctly, adding something along the lines of declare global { namespace NodeJS { ... }} should allow you to have both the global namespace augmentation and your module exports.
How do I use interfaces from external modules as types? I want spotConfig of 'Sequelize.Sequelize' type. Now how do I do that?
This won't help for implicit global references, e.g. spotConfig instead of global.spotConfig. To fix this, add declare const spotConfig: any;.
|
83

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);

4 Comments

This is the better answer. Just as extending Window client-side should use declare global.
This is better, tnx. is there any document about this syntax, declare global?
I get an error "A declare modifier cannot be used in already ambient context" .
declare global seems a invalid syntax
29

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"
    ]
}

1 Comment

I am getting the error: TS2339: Property 'myConfig' does not exist on type 'Global'. with it.
16

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}`)

Comments

9

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

2 Comments

I am attempting something similar, but in regards to reading a configuration JSON file and then having it globally accessible by all my modules. I'm attempting to figure out a solution where I do not have to "re-read" the json file in each module but can't seem to pull it off.
then it is not Global as we have to import in all the files.
7

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"]
}

Comments

3

I believe this should work

declare global {
  var user: string;
  function greeting(user: string, message: string): string;
}
    
export {};

Make sure you are using the var keyword, and you are setting this up in a d.ts file. e.g global.d.ts.

Don't forget to add the export statement

You can now access this anywhere in your codebase. e.g global.person

1 Comment

"Make sure you are using the var keyword" thanks. Don't use let!
1

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

2 Comments

Looks good, but this gives error: ReferenceError: globalThis is not defined
This did work for me, to define a global emit function from a Ava test written in TypeScript.
0

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.

Comments

0

Extending @alexkor's answer a bit primarily for ErrorUtils in react-native. This is to show an example of how to do it with import of types

Adding this to a global.d.ts

declare var ErrorUtils: import('react-native').ErrorUtils;

Will make it pass the typecheck and also allow IDE completions.

The following is an example of what I had tried earlier but did not work

import { ErrorUtils as IErrorUtils } from 'react-native';
declare var ErrorUtils: IErrorUtils ;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.