在 node.js 中扩展 TypeScript 全局对象
Posted
技术标签:
【中文标题】在 node.js 中扩展 TypeScript 全局对象【英文标题】:Extending TypeScript Global object in node.js 【发布时间】: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
接口的一种方式,但似乎没有任何作用。
【问题讨论】:
【参考方案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
;。以上是关于在 node.js 中扩展 TypeScript 全局对象的主要内容,如果未能解决你的问题,请参考以下文章
使用 Visual Studio 2012 express 进行 TypeScript、node.js 开发
使用 node.js 更正了大型 RESTful API 的可扩展结构
使用 TypeScript 扩展 Express Request 对象