参考资料
- Extending TypeScript Global object in node.js https://stackoverflow.com/questions/35074713/extending-typescript-global-object-in-node-js
- 。。。
问题描述
当我们想要往全局对象身上注入一些成员时,ts 会报错。本文档解决的就是如何解决类似这样的报错问题。
使用 any 绕过 ts 的检查
一种非常简单粗暴的法子,就是通过 any 类型避开 ts 的类型检查,但是这种做法无法对成员 a 进行类型约束。
比如,现在我们希望给 window 对象身上注入一个成员 a,要求 a 是一个字符串类型,使用 any 就没法做到,需要使用 declare
声明语法来扩展全局对象。
一些必要的背景知识
declare global
必须在模块上下文中使用- ts 是如何区分一个文件中的内容处于全局上下文还是模块上下文的?
- 如果一个文件中没有使用
import
或export
语句,那么文件中的内容将被视为全局范围。 - 如果一个文件中使用了至少一个
import
或export
语句,那么该文件中的所有内容都将被视为模块范围。
- 如果一个文件中没有使用
- 浏览器环境的全局对象
window
,在 ts 中,对应的接口为Window
- nodejs 环境的全局对象
global
,在 ts 中,对应的接口为NodeJS.Global
- 在 node 16 中,
NodeJS.Global
该接口已经被启用了。
- 在 node 16 中,
扩展浏览器环境下的 window
// ./1.d.ts
export {}
declare global {
interface Window {
a: string
}
}
// ./1.ts
window.a = '1'
// (property) Window.a: string
采用下面这种简写也是 ok 的。
// 1.d.ts
interface Window {
a: string
}
// 1.d.ts
export {}
declare global {
var a: string
}
扩展 node 环境下的 global
首先,需要初始化一下工程:
- 新建一个空目录
- 完成初始化
npm init -y
- 下载 node 的声明文件
npm i @types/node
- 准备俩文件
./1.ts
./1.d.ts
// 1.d.ts
export {}
declare global {
var a: string
}
// 1.ts
global.a = '1'
// ts 能够推导出 var a: string
其实上述写法有些不严谨,这么写相当于直接在全局上下文扩展了一个 a,即便你不通过 global.a
的方式来访问,依旧可以获取到 a,并且推导出的类型也是 string
。
因此,上述写法其实直接用声明全局变量的 declare 语句来写也行
declare var a: string
被弃用的写法:
export {}
declare module NodeJS {
interface Global {
a: string
}
}
按照需求来看的话,这种写法显然更 nice,因为它确确实实在扩展全局对象 global
,并没有污染全局环境。但是很无奈,这玩意儿已被弃用了,暂时还没找到更 nice 的写法。