201

I would like to store my NodeJS config in the global scope.

I tried to follow this => Extending TypeScript Global object in node.js and other solution on stackoverflow,

I made a file called global.d.ts where I have the following code

declare global {
    namespace NodeJS {
      interface Global {
          config: MyConfigType
      }
    }
  }

Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.ts(2669)

but doing this works fine =>

declare module NodeJS  {
    interface Global {
        config: MyConfigType
    }
}

the problem is, I need to import the file MyConfigType to type the config, but the second option do not allow that.

matronator
  • 465
  • 6
  • 13
Bobby
  • 4,372
  • 8
  • 47
  • 103
  • 4
    Understand that an "external module" is a file containing an `import` or `export` statement, that an "ambient module declaration" reads `declare module "m" {}` (note the quotes), and reread the error message. – Aluan Haddad Jul 21 '19 at 10:59
  • 8
    You might need `export {}` – Polv Oct 10 '19 at 19:21
  • 5
    In an ambient decl. file that's not been turned into a module you are already operating in the global scope (outside of declare module {} braces) so you can just omit declare global – Dominic Mar 07 '20 at 13:54

3 Answers3

401

You can indicate that the file is a module like so:

export {};

declare global {
    namespace NodeJS {
        interface Global {
            config: MyConfigType
        }
    }
}
sshh
  • 5,964
  • 4
  • 17
  • 20
  • 36
    wow, works! What trickery is this "export {};". Can you provide some information that I can read up on as to why that works? – user1949561 Jul 11 '20 at 02:52
  • 11
    I think the trick is just that an "external module" is a file containing an import or export statement, so this makes it an "external module". It does seem a little strange that this is needed though... – Tyler Rick Sep 04 '20 at 17:56
  • 3
    @user1949561 here's a good explanation of why https://stackoverflow.com/a/42257742/1048847 – okcoker Nov 28 '21 at 21:52
  • 1
    I think that declaring `MyConfigType` in another file and then using `import` should fix the issue too. (if this option is applicable to your case) – mikey Feb 14 '22 at 04:24
44

Or if you're trying to add a global type within the browser context:

export {};

declare global {
  interface Window {
    ENV: any;
  }
}
Ben Winding
  • 10,208
  • 4
  • 80
  • 67
  • Hmmm. Just tried this but every time I close the .d.ts file the variable that's on the window is underlined in red as an error. The error goes away when I open the window. – LJD Feb 22 '22 at 01:38
  • @LJD try adding the file to tsconfig.json: `"include": ["globals.d.ts"]` – Isaac Jan 30 '23 at 22:09
  • Thanks, that probably would have done it. – LJD Jan 31 '23 at 05:06
4

If your .d.ts is executing as a "script" (it has no import/export statements), you won't need to declare global at all, and can simply remove it. A "script" is already executing in the global context.

(aside: this is why adding export {} works, it turns it from a "script" into a "module" and your declarations in the .d.ts are no longer global by default)

With an example modifying console:

Before:

declare global {
  interface Console {
    log2: (...args: any[])=>void;
  }
}

After:

// This will be global!
interface Console {
  log2: (...args: any[])=>void;
}

Thanks to @okcoker and https://stackoverflow.com/a/42257742/2759427 for the explanation of .d.ts contexts

Cobertos
  • 1,953
  • 24
  • 43