问题引入
写过ts的小伙伴应该都遇到过这个问题:
编辑器报错:Augmentations for the global scope can only be directly nested in external modules or ambient module declarations。 翻译成中文是:全局范围的扩大仅可直接嵌套在外部模块中或环境模块声明中
网上很容易找到解决方法:
But Why?这里的外部模块和环境模块声明又是什么呢?
模块
模块的概念很直接,与nodejs中一样,一个文件就是一个广义上的模块,不过对ts而言,只有ts文件才是一个模块。
ts模块有两种类型:全局模块(环境模块,ambient module)和文件模块(外部模块,external modules)。
怎么区分呢?默认情况下,一个ts文件是全局模块,模块中的类型或者变量声明,处于全局命名空间中,这种情况下,A文件中的变量在B文件中可以使用,因为它们都在全局空间中。
比如文件a.ts中:
// a.ts
const msgA = '这是变量'
type MSG = string
在文件b.ts中可以直接使用:
// b.ts
const msgB:MSG = '这是变量b'
console.log(msgA+msgB);
虽然很神奇,但有时候这种行为很危险。
文件模块:如果在ts文件中有import或者export,则它会在这个文件中创建一个本地作用域。这种情况下,如果B是一个外部模块,A文件想要使用B文件中的变量,则需要先导入。
declare 关键字
再来看ts中的declare关键字。
通过 declare 关键字来告诉 TypeScript,你正在试图表述一个其他地方已经存在的代码。它可以:
- 声明 变量/函数/类/类型等
- 声明模块
- 声明命名空间
- 声明global
问题详解
看这个代码:
// global.d.ts 文件
declare global {
interface Window {
act_name: string;
}
}
我们的目的是:在全局命名空间中,声明Window对象上有一个 act_name 的属性。这里我们用了 declare global
,本来就是多此一举,因为 这个ts文件中没有使用import/export,它是一个全局模块!所以可以直接去掉 declare global,就能达到我们的目的:
// global.d.ts 文件
interface Window {
act_name: string;
}
这是第一种解决方法,简单粗暴。第二种解决方法是:让全局范围的扩大嵌套在外部模块中:
// global.d.ts 文件
declare global {
interface Window {
act_name: string;
}
}
export {}
还剩下一个东西没有弄清楚:全局范围的扩大仅可直接嵌套在环境模块声明中,这个又是什么意思呢?
这里有点歧义,"环境模块声明" 并不是指环境模块文件(全局模块文件),否则的话,下面这个声明就不会报错了:
// global.d.ts 文件
declare global {
interface Window {
act_name: string;
}
}
这里的环境模块声明是指 declare module 语句,嵌套在环境模块声明中,即:
declare module 'aa.ts' {
const a:string;
global {
interface Window {
act_name: string;
}
}
}
上面这种也是合法的,表示 存在一个外部的文件 aa.ts,在其中 声明了全局范围的变量。
另外,除了环境模块声明,还有其他的环境声明:
// ambient module declaration
declare module "mymod" { /*... */ }
// ambient namespace declaration
declare namespace MyNamespace { /*... */ }
// ambient variable declaration
declare const myVar: string;
完结!