#症状
##ブラウザが実行できる機能をTypeScriptが知らないケース
「TypeScriptさん、あなたはTextEncoderを知らないかも知らないけど僕の環境では動くんですよ!あなたが知らないからってエラー吐くのやめてもらえます??」って、TypeScriptではどう書いたらいいんでしょうか。
— 岡竜之介 (@agajo) 2018年5月27日
こちら、僕の最近のツイートですけれども。
例えば、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'.
TextEncoder
やNavigator
の部分は、場合によって変わってきます。
これらのエラーメッセージは、どちらも「そんなものはない。」と言ってきているわけですね。
でもこれ、TypeScriptの翻訳マシンがTextEncoder
やnavigator.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
という拡張子のファイルに宣言だけをまとめて書いておくことができるそうです。
#参考サイト