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

105

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

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

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

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

我不想这样做:

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

我应该如何扩展Global类型,以包含myConfig,或者只是告诉TypeScript闭嘴并相信我?我更喜欢第一种方法。

我不想改变node.d.ts中的声明。我看到了这个SO帖子,尝试了以下操作:

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

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

10个回答

133

node@16 版本开始,已删除 NodeJS.Global 接口,改为使用globalThis

您可以在模块文件中声明新的全局变量,例如:

declare global {
  var NEW_GLOBAL: string;
}

在非模块文件中(没有顶层导入/导出),代码如下:

declare var NEW_GLOBAL: string;

重要提示:变量必须声明为varletconst变量不会显示在globalThis上。


10
截至2021年9月,这是唯一可行的答案!(Node v14 LTS) - codejedi365
3
在TS声明的上下文中,关于"var"的观点非常好。 - Estus Flask
2
不起作用,编译器仍然会报错,并显示以下信息:“Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature”。 - Anthony Luzquiños
2
如何在 eg. UserController 中使用在 Server.ts 中声明和设置的 NEW_GLOBAL?我需要以某种方式导入它吗?Typescript 如何知道它已经在 Servert.s 中声明过了? - Suisse
1
我不确定如何在Node18中实现它,最终我使用了@ts-ignore,这些东西过于复杂了。 - Vadorequest
显示剩余4条评论

94

我看到这篇SO帖子并尝试了以下方法:

你可能会有类似于vendor.d.ts的文件:

// some import 
// AND/OR some export

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

您的文件需要清理掉任何根级别的importexports。这将把该文件转换为一个模块,并将其与全局类型声明命名空间分离。

更多信息:https://basarat.gitbooks.io/typescript/content/docs/project/modules.html


1
我还无法完全弄清楚这个问题。我没有 vendor.d.ts 文件,所以我自己创建了一个,但好像没有起作用。我应该引入它吗? - Justin
2
NodeJS 被声明为一个命名空间,而不是一个模块。 - marvinhagemeister
4
@basarat - 关于“您的文件必须清除任何根级别的导入或导出”的问题,是否可以参考 https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html中的“全局修改模块”来解决?如果我正确理解了那个例子,添加类似于`declare global { namespace NodeJS { ... }}`的内容应该允许您同时具有全局命名空间扩充和模块导出。 - Jan Molak
1
我如何将外部模块的接口用作类型?我想要'Sequelize.Sequelize'类型的spotConfig。现在我该怎么做? - Sid Verma
1
这对于隐式全局引用是没有帮助的,例如 spotConfig 而不是 global.spotConfig。要解决这个问题,请添加 declare const spotConfig: any;。 - Oliver Joseph Ash
显示剩余8条评论

83

为了避免出现这样的Typescript错误:

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

我建议定义自定义类型。我在我的项目中使用src/types/custom.d.ts文件进行定义:

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

然后我确保这些在tsconfig.json文件中被Typescript考虑:

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

现在您可以放心使用自定义属性:

console.log(global.myConfig.a);

6
这是更好的答案。就像客户端扩展Window应该使用declare global一样。 - Robert Penner
这样更好,谢谢。有关于declare global语法的文档吗? - Leon
2
我收到一个错误信息:“不能在已经环境上下文中使用声明修饰符”。 - Mulperi
3
"declare global" 似乎是无效的语法。 - tom10271

29
将以下文件放入我们项目的根目录中是可行的。 global.d.ts
declare namespace NodeJS {
  export interface Global {
    myConfig: any
  }
}

我们正在使用"@types/node": "^7.0.18"和TypeScript Version 2.3.4。我们的tsconfig.json文件如下:

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

2
我遇到了错误:TS2339: Property 'myConfig' does not exist on type 'Global'. - Benny Code

15
使用“namespace”代替“module”声明自定义TypeScript。 如果您使用上述答案并且正在使用较新版本的TypeScript,则会收到有关使用“module”的提示。您应该考虑改用名称空间。 为了满足此处的要求,您实际上需要比扩展全局接口更多的内容。如果您希望直接从“globalThis”上下文中访问它,则还需要创建具有类型的常量。 注意:尽管OP询问了一个对象文字,但该过程与下面所示的过程相同。而不是“Debug”类型是一个函数,您只需按需定义接口,然后将“debug:”更改为myConfig或任何您希望的内容。
// 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
}

以上示例的用途

为了完整起见,在这个示例中我们只是定义了一个全局变量,以便可以记录一个简单的调试消息。以下是如何将该方法绑定到我们的全局上下文中。

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

我们也可以直接调用全局调试方法:

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

9
我唯一有效的方法是这样的: ```html

这是我唯一有效的方法:

```
// lib/my.d.ts

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

并将其用于其他文件中,如下所示。
import {CDTGlobal} from "../lib/my.d.ts";
declare const global: CDTGlobal;

const cwd = global.cdtProjectRoot; // works

我正在尝试类似的事情,但是涉及到读取配置JSON文件,然后使其在所有模块中全局访问。我正在尝试找出一种解决方案,在其中每个模块都不必“重新读取”JSON文件,但似乎无法实现。 - Chason Arthur
那么它就不是全局的,因为我们必须在所有文件中导入它。 - TechTurtle

7

我个人的经验是:

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

global.myConfig = 'it works!';

唯一的缺点是使用它时必须关闭ESLint规则@typescript-eslint/no-namespace

以下是我的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"]
}

0

我可以获得类型检查和代码智能。

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

但接口Global指向global.GLOBAL

您可以获得正确的类型检查,这是因为:

declare var global: NodeJS.Global & typeof globalThis;

但是,除非使用global.GLOBAL.property,否则无法通过使用global.property来获得更好的代码智能。

因此,您需要定义全局变量global并扩展接口Global

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

现在,当您键入global.时,您可以获得property智能。

0
复制我的另一篇文章的答案: globalThis是未来。
// Way 1
var abc: number
globalThis.abc = 200 // no error

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

2
看起来不错,但是出现了错误:ReferenceError: globalThis未定义。 - TechTurtle
这对我有用,可以在 TypeScript 编写的 Ava 测试中定义全局 emit 函数。 - Adrien Joly

0

我相信这应该会起作用

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

确保您正在使用var关键字,并且您正在一个d.ts文件中设置它,例如global.d.ts

别忘了添加导出语句

现在您可以在代码库中的任何地方访问此变量,例如global.person


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接