看了網上不少資料,大多以訛傳訛,完全不能用;因此查閱文檔解決了之後記錄一下。
問題的場景是這樣的,做一個Vue + TS的項目,需要引入一些UMD庫(需要在HTML中通過<script>
標籤引入)。以高德地圖爲例,它的文檔是這樣的:
在頁面添加 JS API 的入口腳本標籤,並將其中「您申請的key值」替換爲您剛剛申請的 key;
<script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.15&key=您申請的key值"></script>
這種庫的問題在於,相當於全局注入(掛載在window上),沒有類型提示,無法在TS中使用。所以我就想着寫一個聲明文件來進行擴展。所以按照網上的說法,我就寫了這樣的代碼:
// shims-global.d.ts
declare global {
interface AMap {
convertFrom: any;
}
const AMap: AMap
interface Window {
AMap: AMap;
}
}
不要在意這個any,只是爲了先讓項目跑起來而已……
但是到了這裏,就遇到了標題裏的那個報錯:TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations。從字面意思來看,只能在外部模塊或者環境模塊的聲明中對全局作用域進行擴展。但是這是啥意思呢?且待我慢慢道來。
事實上這個涉及到TS的模塊機制。TS的文檔是這麼說的:
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).
意思是,如果最上面不是import或者export,這個文件會被當成一個全局的TS腳本,而不是模塊。所以,之前的寫法中,最上面只有一個declare,會被編譯器當成是一個腳本。而在TS中,能夠進行類型擴展的只有interface、namespace和module,腳本是不能進行類型擴展的,所以就會報錯。
解決方法也非常簡單,讓它變成一個模塊就行了。可以在上面加一些無意義的import,或者加個export,都行。比如:
// shims-global.d.ts
export {}
// 或者也可以這麼寫,隨便import個什麼東西
// import Vue from 'vue'
declare global {
interface AMap {
convertFrom: any;
}
const AMap: AMap
interface Window {
AMap: AMap;
}
}
如果按照Stack Overflow上caseyWebb的說法,叫“we must force tsc to interpret this file as a module”。