出題
問題:以下の状態で型がHoge
のグローバル変数g
が存在することを示す型定義を書き足すにはどのようにすればいいでしょうか?
declare class Hoge {
a: number;
sayA: () => void;
}
正解:
declare class Hoge {
a: number;
sayA: () => void;
}
declare var g: Hoge;
問題:ではHoge
の定義が外部モジュールにあってimportする場合、つまり以下の場合に同様のグローバル変数g
が存在することを示す型定義を書き足すにはどのようにすればいいでしょうか?
import { Hoge } from 'hoge-module'
正解:
import { Hoge } from 'hoge-module'
declare global {
var g: Hoge;
}
まだ色々と慣れていないだけかもしれないけど、ちゃんとこれにたどり着くのに結構時間かかってしまった…
解説
前者と後者では同じ型定義ファイルでも前者はスクリプト、後者はモジュールとして扱われる。違いはtop-level importやexportがあるかどうか。
そしてスクリプト上のvarはグローバルスコープである一方、モジュール上のvarはモジュールスコープ。
www.typescriptlang.org
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).
なので、スクリプトの方はdeclare varでグローバル変数となるけども、モジュールの方ではそのままではダメ。
モジュールでグローバル変数を型定義するには以下の通り。
www.typescriptlang.org
ややこしいかもしれないポイント
躓いたのはおそらく、型定義を書きたかっただけなのに型定義ファイルにもスクリプト/モジュールの区別が発生したから(そして発生することを知らなかったから)だと思う。
jsにコンパイルされるファイルであればもちろんjsと同様にスクリプト・モジュールの扱いがあるのはわかるのだけど、型定義だけ独立しているファイルにその区別が発生するのがなかなかしっくりこなかった…
※
どこかでそもそも型定義ファイルはあくまでts
→js
のトランスパイル時にjs
ファイルと合わせて生成されることを想定しており型定義だけを独立して書いたり扱うのは当初の想定外という話を見た気がする(ソースが見つかれば貼る)
であれば型定義ファイルもトランスパイル元のts
(そしてトランスパイルの結果のjs
)がスクリプトならスクリプト、モジュールならモジュールという区別があるのは自然に思う。
※追記
import types(import(...)
型)を使用して以下のようにしてもOK
sample.d.ts
がモジュールではなくグローバルスコープのままになる点でこちらの方がよさそう
declare var g: import('hoge-module').Hoge;
参考文献
import typesについて
www.typescriptlang.org
(import type from ...
と紛らわしく検索はほぼそちらの情報で埋まっているので出てきにくい)
stackoverflow.com
zenn.dev
stackoverflow.com