参考资料
  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. 如果一个文件中没有使用 importexport 语句,那么文件中的内容将被视为全局范围。
    2. 如果一个文件中使用了至少一个 importexport 语句,那么该文件中的所有内容都将被视为模块范围。
  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 的写法。