在 node.js 中扩展 TypeScript 全局对象

我有一个 node.js 应用程序,它将一些配置信息附加到 global对象:

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

TypeScript 编译器不喜欢这样,因为 Global类型没有名为 myConfig的对象:

TS2339: “全局”类型上不存在“ myConfig”属性。

我不想这么做:

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

如何扩展 Global类型以包含 myConfig,或者只是告诉 TypeScript 闭嘴并信任我?我更喜欢第一个。

我不想改变 node.d.ts内部的声明,我看到了这个 所以邮局并尝试了这个:

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

作为扩展现有 Global接口的一种方式,但似乎没有任何效果。

87062 次浏览

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

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

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

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

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

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

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

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.

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.