如何在 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__;
相关推荐
web守墓人41 分钟前
【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库
前端·javascript·html
Savior`L1 小时前
CSS知识复习5
前端·css
许白掰1 小时前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器
中微子5 小时前
🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱
前端·react.js
中微子6 小时前
React 状态管理 源码深度解析
前端·react.js
加减法原则7 小时前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
yanlele8 小时前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
lichenyang4538 小时前
React移动端开发项目优化
前端·react.js·前端框架
你的人类朋友8 小时前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
web_Hsir8 小时前
vue3.2 前端动态分页算法
前端·算法