TypeScript でモジュール内でのみ参照可能なグローバル変数を宣言する2021年03月21日 15時34分

Web ブラウザで使われる JavaScript ライブラリの中には、グローバル変数 (window オブジェクトのプロパティ) をはやすものがあります。Google Tag Manager の window.dataLayer や、Canva ボタンの window.Canva などです。

そうしたグローバル変数をあちこちのモジュールから直接操作していると保守性が下がってしまうので、ラッパーとなるモジュールを用意したいところです。グローバル変数を直接操作するのはラッパーモジュール内のみにとどめ、他のモジュールはラッパーモジュールを介して外部ライブラリにアクセスするという仕組みです。

このとき、ラッパーモジュール内でグローバル変数にアクセスしつつ、他のモジュールではそのグローバル変数にアクセスできないようにするには、どうしたらよいでしょうか。

TypeScript でのグローバル変数の宣言は以下のように declare global を使うのが基本ですが、これだと他のモジュールでも window.dataLayer にアクセスできてしまいます。

declare global {
  interface Window {
    dataLayer: object[];
  }
}

ラッパーモジュール内で以下のように window 変数を宣言しなおすことで、グローバル変数を参照できる範囲がラッパーモジュール内に限定されます。(「グローバル変数」といいつつ、window オブジェクトのプロパティとしてアクセスすることになりますが。)

declare const window: Window['window'] & {
  dataLayer: object[];
};

// このモジュール内では `window.dataLayer.push(...)` と書ける。

Window['window'] はもともとの window オブジェクトの型を指します。それとの交差型 (intersection types) を取ることで、window オブジェクトの既存のプロパティ (window.setTimeout など) にも、新たに宣言したプロパティにもアクセスできます。