在Typescript项目中,你可能都遇到过这个错误:
'Window & typeof globalThis' 类型上不存在属性 'X'。
快速修复方案
我们将介绍几种不同的解决方案来解决这个问题。
Window 接口是在名为 lib.dom.d.ts 的文件中全局定义的。你可以使用各种技术来更改它:
-
要在 .ts或.tsx文件中全局更改它,你可以使用declare global和interface Window:
declare global {
interface Window {
X: number;
}
}
-
要在 .d.ts文件中全局更改它,你只需指定interface Window。
interface Window {
X: number;
}
-
要仅为一个文件更改它,你可以在 .ts或.tsx文件中使用declare const window:
declare const window: {
X: number;
} & Window;
详细原因分析
Window 接口位于 TypeScript 的全局作用域中。它作为 DOM 类型的一部分随 lib.dom.d.ts 提供,描述了浏览器中可用的方法。
当第三方脚本(或我们自己的代码)向window添加内容时,问题就出现了:
window.X = Date.now();
这是一个问题,因为 TypeScript 不知道 X 属性,所以当你尝试访问它时会抛出错误:
所以,我们需要以某种方式更改 Window 的全局定义,以包含 TypeScript 不知道的新属性。
解决方案 1:在 .ts 或 .tsx 文件中使用 declare global
第一个解决方案是使用 declare global 来更改 Window 的全局定义:
declare global {
interface Window {
X: number;
}
}
这之所以有效有两个原因。首先,declare global 告诉 TypeScript,其中的内容应该添加到全局作用域中。
其次,我们利用了声明合并。这是 TypeScript 中的一个规则,同一作用域中同名的接口会合并。因此,通过在同一作用域中重新声明 Window,我们可以向它添加新属性。
如果我们使用 type,这将不起作用,因为类型不支持声明合并。
这个解决方案只在 .ts 或 .tsx 文件中有效,因为 declare global 只在模块内工作。所以这个解决方案在项目中放置定义的位置有点尴尬。在自己的模块中?与其他地方一起?
解决方案 2:.d.ts 文件
一个更整洁的解决方案是使用 .d.ts 文件中的 interface Window:
// window.d.ts(选择你喜欢的任何文件名)
interface Window {
X: number;
}
这之所以有效,是因为你在 .d.ts 中放置的任何内容都会自动进入全局作用域,所以不需要 declare global。
它还允许你将全局定义放在一个单独的文件中,这比试图弄清楚要放在哪个 .ts 文件中要干净一些。
解决方案 3:单模块覆盖
如果你担心将类型添加到全局作用域,你可以使用 declare const window 在单个模块中覆盖 window 的类型:
declare const window: {
X: number;
} & Window;
declare const window 就像 const window,但在类型层面上。所以它只作用于当前作用域——在这种情况下,是模块。我们声明类型为当前的 Window 加上新属性 X。
如果你需要在多个文件中访问 window.X,这个解决方案会有点烦人,因为你需要在每个文件中添加 declare const window 行。
我应该使用哪种解决方案?
就我个人而言,我倾向于使用解决方案 2——一个 .d.ts 文件。它是代码行数最少的,也最容易放在项目中。
我不介意将类型添加到全局作用域。通过实际更改 Window 的类型,你更准确地描述了你的代码执行的环境。在我看来,这是一个加分项。
但如果你真的担心这个问题,使用 declare const 解决方案。
本文由 mdnice 多平台发布



















