每当这时,你可能想在TypeScript中静态地输入一个全局变量。例如,在我的一些网络应用中,我需要从服务器上渲染的标记中传递一些属性给在浏览器中运行的JavaScript代码。要做到这一点,我通常会在一个内联脚本中定义一个名为__INITIAL_DATA__ 的全局变量,并将一个JSON序列化的对象分配给它。
<script>
window.__INITIAL_DATA__ = {
userID: "536891193569405430",
};
</script>
现在,如果我试图在TypeScript文件中访问window.__INITIAL_DATA__ ,编译器会产生一个类型错误,因为它无法在任何地方找到__INITIAL_DATA__ 属性的定义。
// Property '__INITIAL_DATA__' does not exist
// on type 'Window & typeof globalThis'
const initialData = window.__INITIAL_DATA__;
我将向你展示几种不同的方法,让TypeScript知道window.__INITIAL_DATA__ 属性,并使类型错误消失。
使用一个类型断言
让类型错误消失的最快方法是在一个类型断言中使用any 类型。我们可以把window 对象看作是any 类型,这样我们就可以访问它的__INITIAL_DATA__ 属性。
const initialData = (window as any).__INITIAL_DATA__;
这个解决方案是有效的,而且我们不再得到一个类型错误。如果你需要一个临时的方法来访问TypeScript不知道的window 对象上的一个属性,这是一个实用的方法。
(window as any).__INITIAL_DATA__ 表达式是类型any ,因此initialData 也是类型any 。我们可以更进一步,使用另一个类型断言来给initialData 变量一个更具体的类型。
type InitialData = {
userID: string;
};
const initialData = (window as any).__INITIAL_DATA__ as InitialData;
现在,我们可以以一种类型安全的方式访问initialData.userID 。
const userID = initialData.userID; // Type string
请记住,这并不能保证window.__INITIAL_DATA__ 在运行时被正确设置。类型检查器信任我们,我们的工作是确保我们将一个具有预期形状的对象分配给window.__INITIAL_DATA__ 。
声明一个全局变量
另一种方法是使用declare var 语法来声明一个全局变量。这样,我们可以让TypeScript知道,它可以期望找到一个具有给定名称和类型的全局变量。
declare var __INITIAL_DATA__: InitialData;
我们现在可以直接访问__INITIAL_DATA__ 变量 ...
const initialData = __INITIAL_DATA__;
...或者从window 对象上访问。
const initialData = window.__INITIAL_DATA__;
注意,在ECMAScript模块中,通过window.__INITIAL_DATA__ 的访问是不工作的。如果你的JavaScript文件包含顶层的import 或export 声明,它就被认为是一个模块,如果你试图访问window 对象上的__INITIAL_DATA__ ,你会收到一个类型错误。
你可以通过使用declare global { ... } 语法在全局范围内声明一个全局变量,以便能够在一个JavaScript模块中直接访问window.__INITIAL_DATA__ 以及__INITIAL_DATA__ 。
export function someExportedFunction() {
// ...
}
declare global {
var __INITIAL_DATA__: InitialData;
}
const initialData = window.__INITIAL_DATA__;
如果你需要在多个文件或模块中访问window.__INITIAL_DATA__ ,在你的项目中创建一个globals.d.ts文件可能是个好主意。在该文件中,你可以声明所有你要使用的全局变量。
declare var __INITIAL_DATA__: InitialData;
只要globals.d.ts是你的 TypeScript 项目的一部分,编译器就会知道__INITIAL_DATA__ 是一个全局变量,而且它会让你通过__INITIAL_DATA__ 以及window.__INITIAL_DATA__ 来访问它。
增强窗口界面
最后,你可以使用TypeScript的接口声明合并来让编译器知道,它可以期望在Window 类型上找到一个名为__INITIAL_DATA__ 的属性,从而找到window 对象。要做到这一点,你需要定义一个名为Window 的接口和一个名为__INITIAL_DATA__ 的属性。
interface Window {
__INITIAL_DATA__: InitialData;
}
TypeScript 将把这个接口定义与lib.dom.d.ts 中定义的Window 接口合并在一起,从而形成一个单一的Window 类型。现在,下面的赋值将不再产生一个类型错误。
const initialData = window.__INITIAL_DATA__;
请注意,这种方法在JavaScript模块中是行不通的。你需要再次使用declare global { ... } 语法,以使window.__INITIAL_DATA__ 表达式能够正确地进行类型检查。
export function someExportedFunction() {
// ...
}
declare global {
interface Window {
__INITIAL_DATA__: InitialData;
}
}
const initialData = window.__INITIAL_DATA__;