JavaScript
TypeScript

TypeScriptの翻訳機が知らないような機能を使う時は、アンビエント宣言をしておく


症状


ブラウザが実行できる機能をTypeScriptが知らないケース

こちら、僕の最近のツイートですけれども。

例えば、TypeScriptで次のようなコードを書くと、

let encoder:any = new TextEncoder()

navigator.clipboard.writeText(this.modified_output);

これらのコードがJavaScriptに翻訳される際に、次のようなエラーがでることがあります。

Cannot find name 'TextEncoder'.

Property 'clipboard' does not exist on type 'Navigator'.

TextEncoderNavigatorの部分は、場合によって変わってきます。

これらのエラーメッセージは、どちらも「そんなものはない。」と言ってきているわけですね。

でもこれ、TypeScriptの翻訳マシンがTextEncodernavigator.clipboardを知らないだけで、このエラーを無視して翻訳を実行してしまえば、ブラウザ上ではきちんと動くんですよ。


htmlから他のjsファイルを読み込む予定があるケース

他にも、Google Analyticsのga関数を使う場合や、jQueryの$関数を使う場合など、

それらの関数を定義している外部jsファイルを、html上で先に読み込むつもりでいる場合なんかも、

TypeScriptを翻訳する瞬間にはその外部jsファイルがないわけですから、

TypeScript翻訳機からしてみれば「そんなの知らないよ!」ということになって、同様のエラーが出ます。


アンビエント宣言

対処法としては、「このコードではこういう機能を使います。翻訳機さんは知らないだろうけど、別の所でちゃんと定義される予定だから、エラーは出さないでね。」

という意味で「アンビエント宣言」というものを書いておけば良いです。

クラス定義内ではなく、.tsファイルの最初の方でアンビエント宣言を書いておきましょう。

書き方は、「行頭にdeclareをつけて、変数(関数、クラスなど)を宣言する」です。

僕は今の所、インポート文の後に書くようにしてます。

例えばこんな感じ。

declare function TextEncoder():void;

declare var navigator:any;
declare let ga: any;

ちなみに英単語 "ambient" は形容詞で、「包囲した」「取り巻く」「周囲の」「周囲を取り巻く」といった意味です。

「こういうの名前の関数を使うけど、定義はこのファイルの外(周囲)にあるから、ここには出てこないよ。」という雰囲気ですかね。

アンビエント宣言は変数や関数に限らず、クラスやモジュールに対してもできます。

また、.d.tsという拡張子のファイルに宣言だけをまとめて書いておくことができるそうです。


参考サイト

https://docs.solab.jp/typescript/ambient/declaration/