【问题标题】:Extending TypeScript Global object in node.js在 node.js 中扩展 TypeScript 全局对象
【发布时间】:2016-05-06 14:37:24
【问题描述】:

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

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

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

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

我不想这样做:

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

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

我不想更改node.d.ts 中的声明。我看到了这个SO post 并尝试了这个:

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

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

【问题讨论】:

    标签: node.js typescript global


    【解决方案1】:

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

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

    但是接口Global指向global.GLOBAL。

    你能得到正确的类型检查,是因为:

    declare var global: NodeJS.Global & typeof globalThis;
    

    但是你不能通过使用global.property获得更好的代码智能,除非使用global.GLOBAL.property。

    因此,您需要同时定义全局变量global 和扩展接口Global:

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

    现在你可以在输入global. 时获得property 情报

    【讨论】:

      【解决方案2】:

      截至node@16 NodeJS.Global 接口has been removed 支持globalThis。

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

      declare global {
        var NEW_GLOBAL: string;
      }
      

      并且在非模块文件中(没有顶级导入/导出)为:

      declare var NEW_GLOBAL: string;
      

      重要提示:变量必须声明为var。 let 或 const 变量不会显示在 globalThis。

      【讨论】:

      • 这是截至 2021 年 9 月唯一有效的答案! (节点 v14 LTS)
      • 在 TS 声明的上下文中,关于 var 的观点非常好。
      • 救了我的培根。谢谢!
      • 不适合我,编译器仍然抱怨以下消息:'元素隐式具有'任何'类型,因为类型'typeof globalThis'没有索引签名'。
      • 以及如何使用在 Server.ts 中声明和设置的 NEW_GLOBAL。用户控制器?我需要以某种方式导入它吗? Typescript 是如何知道它已经在 Servert.s 中声明的?
      【解决方案3】:

      使用“命名空间”而不是“模块”来声明自定义 TypeScript

      如果您使用上述任何答案并且使用的是较新版本的 Typescript,您将会对使用 "module" 感到厌烦。你应该考虑命名空间。

      为了满足这里的要求,您实际上需要的不仅仅是扩展 Global 接口。如果您希望直接从 "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}`)
      

      【讨论】:

        【解决方案4】:

        对我有用的是:

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

        【讨论】:

          【解决方案5】:

          复制我对another post的回答:

          globalThis 是未来。

          // Way 1
          var abc: number
          globalThis.abc = 200 // no error
          
          // Way2
          declare var age: number
          globalThis.age = 18 // no error
          

          【讨论】:

          • 看起来不错,但这会报错:ReferenceError: globalThis is not defined
          • 这确实对我有用,可以从用 TypeScript 编写的 Ava 测试中定义一个全局 emit 函数。
          【解决方案6】:

          唯一对我有用的是:

          // 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 文件,但似乎无法完成。
          • 那么它不是全局的,因为我们必须在所有文件中导入。
          【解决方案7】:

          将以下文件放入我们项目的根目录是可行的。

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

          【讨论】:

          • 我收到错误消息:TS2339: Property 'myConfig' does not exist on type 'Global'.。
          【解决方案8】:

          为了避免 Typescript 声明这样的事情:

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

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

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

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

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

          现在您可以安全地使用自定义属性了:

          console.log(global.myConfig.a);
          

          【讨论】:

          • 这是更好的答案。正如扩展Window 客户端应该使用declare global。
          • 这样更好,tnx。有没有关于这种语法的文档,declare global?
          • 我收到一个错误“A declare modifier cannot be used in already environment context”。
          • declare global 似乎语法无效
          【解决方案9】:

          我看到了这个 SO 帖子并尝试了这个:

          你可能有类似vendor.d.ts:

          // some import 
          // AND/OR some export
          
          declare module NodeJS  {
              interface Global {
                  spotConfig: any
              }
          }
          

          您的文件需要 干净 任何根级别 import 或 exports。这会将文件变成一个模块,并断开它与全局类型声明命名空间的连接。

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

          【讨论】:

          • 我不太明白这一点。我没有vendor.d.ts,所以我做了一个,但它似乎没有效果。我应该导入它吗?
          • NodeJS 被声明为命名空间,而不是模块
          • @basarat - RE:“您的文件需要清除任何根级别的导入或导出”是否无法按照typescriptlang.org/docs/handbook/declaration-files/templates/… 在此处应用“全局修改模块”?如果我正确理解了该示例,则添加类似declare global { namespace NodeJS { ... }} 的内容应该允许您同时拥有全局命名空间扩充和模块导出。
          • 如何使用外部模块的接口作为类型?我想要 'Sequelize.Sequelize' 类型的 spotConfig。现在我该怎么做?
          • 这对隐式全局引用没有帮助,例如spotConfig 而不是 global.spotConfig。要解决此问题,请添加 declare const spotConfig: any;。
          猜你喜欢
          • 2020-04-17
          • 1970-01-01
          • 2021-05-05
          • 2021-05-06
          • 1970-01-01
          • 2020-05-15
          • 2014-10-06
          • 2011-05-07
          • 1970-01-01
          相关资源
          最近更新 更多