参考资料
  1. Extending TypeScript Global object in node.js https://stackoverflow.com/questions/35074713/extending-typescript-global-object-in-node-js
  2. 。。。

问题描述

image.png

当我们想要往全局对象身上注入一些成员时,ts 会报错。本文档解决的就是如何解决类似这样的报错问题。

使用 any 绕过 ts 的检查

image.png

一种非常简单粗暴的法子,就是通过 any 类型避开 ts 的类型检查,但是这种做法无法对成员 a 进行类型约束。

比如,现在我们希望给 window 对象身上注入一个成员 a,要求 a 是一个字符串类型,使用 any 就没法做到,需要使用 declare 声明语法来扩展全局对象。

一些必要的背景知识
  1. declare global 必须在模块上下文中使用
  2. ts 是如何区分一个文件中的内容处于全局上下文还是模块上下文的?
    1. 如果一个文件中没有使用 import 或 export 语句,那么文件中的内容将被视为全局范围。
    2. 如果一个文件中使用了至少一个 import 或 export 语句,那么该文件中的所有内容都将被视为模块范围。
  3. 浏览器环境的全局对象 window,在 ts 中,对应的接口为 Window
  4. nodejs 环境的全局对象 global,在 ts 中,对应的接口为 NodeJS.Global
    1. 在 node 16 中,NodeJS.Global 该接口已经被启用了。

扩展浏览器环境下的 window
  1. // ./1.d.ts
  2. export {}
  3. declare global {
  4. interface Window {
  5. a: string
  6. }
  7. }
  1. // ./1.ts
  2. window.a = '1'
  3. // (property) Window.a: string

采用下面这种简写也是 ok 的。

  1. // 1.d.ts
  2. interface Window {
  3. a: string
  4. }
  1. // 1.d.ts
  2. export {}
  3. declare global {
  4. var a: string
  5. }

扩展 node 环境下的 global

首先,需要初始化一下工程:

  1. 新建一个空目录
  2. 完成初始化 npm init -y
  3. 下载 node 的声明文件 npm i @types/node
  4. 准备俩文件 ./1.ts ./1.d.ts
  1. // 1.d.ts
  2. export {}
  3. declare global {
  4. var a: string
  5. }
  1. // 1.ts
  2. global.a = '1'
  3. // ts 能够推导出 var a: string

image.png

其实上述写法有些不严谨,这么写相当于直接在全局上下文扩展了一个 a,即便你不通过 global.a 的方式来访问,依旧可以获取到 a,并且推导出的类型也是 string。

image.png

因此,上述写法其实直接用声明全局变量的 declare 语句来写也行

  1. declare var a: string

被弃用的写法:

  1. export {}
  2. declare module NodeJS {
  3. interface Global {
  4. a: string
  5. }
  6. }

按照需求来看的话,这种写法显然更 nice,因为它确确实实在扩展全局对象 global,并没有污染全局环境。但是很无奈,这玩意儿已被弃用了,暂时还没找到更 nice 的写法。