UMD 的包如何導出 TS 類型

在 TypeScript 裏聲明模塊,最早是用 namespace 和 module 的語法,後來支持了 es module,類型和變量會用 import 來導入、用 export 導出。

比如你寫了一個庫,導出的變量叫 Guang,它下面有 name 和 age 兩個屬性,所以你是這樣聲明類型的:

export default Guang;

declare namespace Guang {
    export const name = 'guang';
    export const age = '20';
}

使用的時候用 import 來導入:

import Guang from 'xxx';

console.log(Guang.name, Guang.age);

這樣是沒啥問題。

但如果這個庫除了支持 es module 的方式使用,還支持 umd 呢?

UMD 規範想必大家很熟悉了,就是判斷是 CMD、AMD 還是全局變量的方式,然後用合適的模塊規範導出模塊的值:

但這裏面不包含 es module,因爲它不是 api 而是語法。

那如果你構建出了 umd 規範的代碼,使用者用 script 的方式給引入了:

這樣還能做類型提示和檢查麼?

不能了,因爲你導出是用的 esm 的 export,只有 import 引入纔會有類型提示和對應的檢查。

那怎麼辦呢?

用 declare global 聲明爲全局類型?

declare global {
    namespace Guang {
        export const name = 'guang';
        export const age = '20';
    }
}

這樣是能解決問題,但這樣在 esm 模塊裏也不用 import 就可以直接用了,而我們想在 esm 裏用 import,其他情況才用全局類型。

有啥方式能約束在 esm 裏只能 import 用,但是其他地方可以做爲全局類型呢?

TypeScript 專門爲這種情況設計個了語法,叫 export as namespace xxx;

比如上面的代碼可以這樣寫:

export = Guang;
export as namespace Guang;

declare namespace Guang {
    export const name = 'guang';
    export const age = '20';
}

export = Guang 是兼容老的 ts import 語法的,支持 umd 得加上這一行,然後加上 export as namespace Guang;

這樣你在非 esm 裏就可以通過全局類型的方式使用它了:

而在 esm 裏,如果也是這樣用的,它會報錯:

說是你在 esm 模塊裏用了一個 UMD 的 global 類型,建議用 import 的方式代替。

如果你用 import 的方式引入了這個類型,就不報錯了:

這就是它比 declare global 好的地方,可以約束在 esm 裏用 import 引入,非 es module 可以作爲全局類型。

這樣就完美兼容了 esm 和 umd 兩種模塊引入方式。

而且如果你不想要這種限制,也可以在 tsconfig.json 裏關掉。

有個 allowUmdGlobalAccess 的編譯選項就是控制是否支持在 es module 裏使用 UMD 全局類型的:

默認是 false,開啓以後在 es module 裏使用 UMD 全局類型就不報錯了:

很多庫都需要兼容 esm 和 umd 的使用方式都會這樣用,比如 react:

所以,如果你開發的庫需要支持 esm 和 umd 的話,可以用 export namespace as xxx 來導出,會比 declare global 更好。

總結

現在 TypeScript 的模塊都是 es module 的方式引入的,但有一些包是支持 umd 的,它們可能用各種方式引入模塊,爲了實現 umd 模塊的類型檢查,可以用 declare global 把導出的變量變爲全局的。

但是在 es module 裏還是希望使用 import 引入,非 es module 才用全局類型,所以更好的方式是使用 export as namespace xxx。

用這種方式聲明的類型,當在非 esm 中使用時,會作爲全局類型,而在 esm 中如果直接引用全局類型會報錯,建議用 import 引入。這是它比 declare global 更好的地方。

當然,也可以把 allowUmdGlobalAccess 的編譯選項設置爲 true 來放開這種約束。

像 react 這種支持 umd 的庫都是用這種方式導出類型的,如果你也要開發一個支持 umd 的庫,不妨也試試 export as namespace 吧。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MAVKbCFSjjfbDhmlDrXImQ