如何在 TypeScript 中声明全局变量?

原文链接:Declaring Global Variables in TypeScript,2020.04.14,by Marius Schulz.

有时候,你可能想在 TypeScript 使用一些自定义全局变量。例如,在我的一些 Web 应用程序中,我需要将一些从服务器端渲染时传递过来的属性的给在浏览器中运行的 JavaScript 代码使用。为了做到这一点,通常我会在内联脚本中定义一个名为 __INITIAL_DATA__ 的全局变量,并将一个 JSON 序列化对象赋值给它:

html 复制代码
<script>
  window.__INITIAL_DATA__ = {
    userID: "536891193569405430",
  };
</script>

现在,如果我尝试在一个 TypeScript 文件中访问 window.__INITIAL_DATA__ 时,编译器就会产生一个类型错误,因为它找不到 __INITIAL_DATA__ 属性的定义。

typescript 复制代码
// Property '__INITIAL_DATA__' does not exist
// on type 'Window & typeof globalThis'
const initialData = window.__INITIAL_DATA__;

接下来,我将向你展示几种不同的方法,让 TypeScript 知道 window.__INITIAL_DATA__ 属性的存在,并消除类型错误。

使用类型断言

消除类型错误的最快方法是在类型断言中使用 any 类型。我们可以将 window 对象视为任意类型,以便访问其 __INITIAL_DATA__ 属性:

typescript 复制代码
const initialData = (window as any).__INITIAL_DATA__;

这个解决方案有效,我们不再遇到类型错误。如果你需要一种临时的方式来访问 TypeScript 不知道的 window 对象上的属性,这是一个实用的方法。

(window as any).__INITIAL_DATA__ 表达式是任意类型,因此 initialData 也是任意类型。我们可以进一步使用另一种类型断言来给 initialData 变量指定更具体的类型:

typescript 复制代码
type InitialData = {
  userID: string;
};

const initialData = (window as any).__INITIAL_DATA__ as InitialData;

现在,我们可以以类型安全的方式访问 initialData.userID

typescript 复制代码
const userID = initialData.userID; // string 类型

请记住,这并不保证 window.__INITIAL_DATA__ 在运行时会被正确设置。类型检查器相信我们,并且我们的工作是确保将一个符合预期形状的对象赋值给 window.__INITIAL_DATA__

声明一个全局变量

另一种方法是使用 declare var 语法声明一个全局变量。这样,我们可以让 TypeScript 找到这个具有给定名称和类型的全局变量:

typescript 复制代码
declare var __INITIAL_DATA__: InitialData;

我们现在可以直接访问 __INITIAL_DATA__ 变量了......

typescript 复制代码
const initialData = __INITIAL_DATA__;

......或通过 window 对象获取:

typescript 复制代码
const initialData = window.__INITIAL_DATA__;

有一点要注意的是,在ECMAScript 模块内部通过 declare var 声明的变量是是无法通过 window.__INITIAL_DATA__ 形式访问的,会收到类型错误。而所谓 ECMAScript 模块就是包含顶级 importexport 声明的那些文件。

你可以使用 declare global { ... } 语法在全局作用域中声明一个全局变量,以便能够在 JavaScript 模块内部或外部文件通过 window.__INITIAL_DATA____INITIAL_DATA__ 两种形式访问。

typescript 复制代码
export function someExportedFunction() {
  // ...
}

declare global {
  var __INITIAL_DATA__: InitialData;
}

const initialData = window.__INITIAL_DATA__;

如果你需要在多个文件或模块中访问 window.__INITIAL_DATA__,那么在项目中创建一个 globals.d.ts 文件可能是个好主意。在这个文件中,你可以声明所有将要使用的全局变量:

typescript 复制代码
declare var __INITIAL_DATA__: InitialData;

增强 Window 接口

最后,你可以使用 TypeScript 的接口声明合并来告诉编译器在 Window 类型上有一个名为 __INITIAL_DATA__ 的属性。为了做到这一点,你需要定义一个名为 Window 的接口,设置一个名为 __INITIAL_DATA__ 的属性:

typescript 复制代码
interface Window {
  __INITIAL_DATA__: InitialData;
}

TypeScript 将会上面的这个接口定义和 lib.dom.d.ts 中定义的 Window 接口合并,从而得到一个单一的 Window 类型。这样一来,下面的赋值就不再产生类型错误了:

typescript 复制代码
const initialData = window.__INITIAL_DATA__;

请注意,这种方法同样 ECMAScript 模块中不起作用。为了使 window.__INITIAL_DATA__ 表达式能够正确进行类型检查,需要再次使用 declare global { ... } 语法:

typescript 复制代码
export function someExportedFunction() {
  // ...
}

declare global {
  interface Window {
    __INITIAL_DATA__: InitialData;
  }
}

const initialData = window.__INITIAL_DATA__;
相关推荐
come1123413 分钟前
Vue 响应式数据传递:ref、reactive 与 Provide/Inject 完全指南
前端·javascript·vue.js
musk12121 小时前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
万少2 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL2 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl022 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang2 小时前
前端如何实现电子签名
前端·javascript·html5
今天又在摸鱼2 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
蓝婷儿2 小时前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端
百锦再2 小时前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref
jingling5552 小时前
面试版-前端开发核心知识
开发语言·前端·javascript·vue.js·面试·前端框架