应通过 declare global { interface window { myplugin: { init(): void; version: string; }; } } export {}; 正确扩展 window 接口,再在运行时赋值,确保类型安全与 ide 支持。

直接给 window 赋值自定义属性(比如 window.myPlugin = {...})会触发 TypeScript 报错,根本原因在于 TypeScript 的类型系统默认只认 lib.dom.d.ts 中定义的 Window 接口,不允许访问未声明的属性。安全扩充的关键是**正确扩展全局 Window 接口**,让类型检查器“知道”这个属性存在且类型明确。
必须用 declare global + interface Window
这是唯一被 TypeScript 官方认可、能真正合并到全局 Window 类型的方式。不能只写 interface Window,也不能放在普通 .ts 文件里——必须包裹在 declare global 块中,并确保文件是模块(即有 export {} 或其他导出)。
- 新建一个
types/window.d.ts(或任意.d.ts文件,路径需被tsconfig.json的include或files覆盖) - 内容严格按如下格式书写:
declare global {<br> interface Window {<br> myPlugin: {<br> init(): void;<br> version: string;<br> };<br> }<br>}<br>export {};
注意:export {}; 是必需的,它告诉 TypeScript 这是一个模块文件,否则会报错 “Augmentations for the global scope can only be directly nested…”
避免常见错误写法
这些方式看似能绕过报错,但会破坏类型安全或 IDE 支持,属于反模式:
-
不要用
// @ts-ignore:完全跳过类型检查,window.myPlugin.init(123)即使参数错也不会提示 -
不要用
(window as any).myPlugin:等同于放弃类型,失去自动补全、跳转和重构能力 -
不要只写
interface Window不加declare global:TypeScript 会认为你是在定义一个新类型,导致重复声明错误 -
不要在普通
.ts文件顶部直接写declare global:若该文件没有export,TS 会忽略它;务必保证是模块上下文
配合运行时赋值,保持类型与实现一致
类型声明只是告诉编译器“它应该长什么样”,实际挂载仍需 JS 代码执行。建议统一管理,避免声明和赋值脱节:
- 在入口文件(如
main.ts)中执行挂载:
window.myPlugin = {<br> init() { console.log('ready'); },<br> version: '1.0.0'<br>};
这样,类型声明 + 实际赋值一一对应,后续任何地方使用 window.myPlugin.init() 都有完整类型提示和校验。
多个插件或配置项可集中声明
如果项目中要挂多个全局变量(如 CDN 地址、API 配置、第三方 SDK 实例),推荐在一个 .d.ts 文件中统一扩展,提高可维护性:
- 例如声明环境常量和工具对象:
declare global {<br> interface Window {<br> __APP_ENV: {<br> API_BASE_URL: string;<br> CDN_HOST: string;<br> };<br> L2Dwidget: {<br> init(config: any): void;<br> };<br> }<br>}<br>export {};
所有声明都参与类型合并,不会冲突,也便于团队协作时查阅全局 API 表面契约。











