如何在 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__;
相关推荐
黑客老陈3 分钟前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
正小安8 分钟前
Vite系列课程 | 11. Vite 配置文件中 CSS 配置(Modules 模块化篇)
前端·vite
暴富的Tdy36 分钟前
【CryptoJS库AES加密】
前端·javascript·vue.js
neeef_se36 分钟前
Vue中使用a标签下载静态资源文件(比如excel、pdf等),纯前端操作
前端·vue.js·excel
m0_7482356143 分钟前
web 渗透学习指南——初学者防入狱篇
前端
z千鑫1 小时前
【前端】入门指南:Vue中使用Node.js进行数据库CRUD操作的详细步骤
前端·vue.js·node.js
m0_748250742 小时前
Web入门常用标签、属性、属性值
前端
m0_748230442 小时前
SSE(Server-Sent Events)返回n ,前端接收数据时被错误的截断【如何避免SSE消息中的换行符或回车符被解释为事件消息的结束】
前端
生产队队长3 小时前
项目练习:element-ui的valid表单验证功能用法
前端·vue.js·ui