Visual Studio 2015 Communityで自作のJavaScriptプログラムをTypeScriptに変換した。
その時に、沢山のエラーが出るのだが、
タイトルに示した、
と言うエラーで悩まされた。
解決してみると簡単な理由だったのだが、ambient or non-ambientって何やねん?
OpenGLの環境光(ambient)か?
関係無いか。
こういうソフトは多分OpenGLで書いてあるんだろうなあ。
ワテも家をデザインしてみたい。
さて、兎に角、英語が苦手なワテには、英文のエラーメッセージが出ると訳分からなくなる。
JavaScript.js の拡張子を JavaScript.ts に変更すると、100個以上のエラーや警告が出た。
それらを一つずつ修正してTypeScript化を図ったのだが、確か冒頭のエラーも以前に見覚えがあるエラーだ。
で、過去のソースを見直して原因と対策を思い出した。
その結果、無事にエラーは解消した。
この記事では、エラー解決までの一連の手順を詳細したい。
では、本題に入ろう。
TypeScriptのmoduleやexportの例
例えば、TypeScriptでモジュールを作成して、そのモジュール内部で定義している変数や関数にexportを付けておくとそれらはモジュール外部の他の場所で参照できる。
// file1.ts "use strict"; module mod1 { export var num1_export: number = 100; // publicみたいなもんか var num2: number = 200; // privateみたいなもんか export function func1_export(msg) { alert(msg); } function func2(msg) { alert(msg); } }
まあ、要するにexportを付けておくとC言語などのpublicみたいな扱いになるのかな。
ちなみに、TypeScriptの文法はJavaScriptの上位互換性があるので、変数定義などは
var num2 = 200; // JavaScript形式
のままTypeScriptに移植しても良い。
その場合は変数num2はanyという変数型になる。
一方、TypeScriptでは変数型をnumber, string, anyなどで明示的に指定して宣言する事も可能なので、例えばnum2を数値型として明示的に宣言したい場合には、
var num2: number = 200; // TypeScript形式
とすれば良い。
さて、このようにmodule mod1が定義してある場合には、別のファイルfile2.tsにおいて、
// file2.ts "use strict"; module mod2 { window.onload = () => { console.log("ONLOAD"); func2(); }; function func2() { var num2 = mod1.num1_export; mod1.func1_export('message from mod2: num2 is ' + num2); //mod1.func2('message from mod2') // エラー Property 'func2' does not exist on type 'typeof mod1'. } }
のようにmodule mod2を定義しておくと、プログラム実行時にはonloadが実行され、その中からfunc2()が実行される。
そのfunc2()の中では、mod1でexportされている変数を参照したり、
var num2 = mod1.num1_export;
あるいは、mod1内のメソッドを実行する事も出来る。
mod1.func1_export('message from mod2: num2 is ' + num2);
つまり、外部に公開しておきたい変数、関数の頭にはexportを付けておくと所謂C, C++, C#などの他のプログラミング言語のpublicみたいな扱いになるのだ。
TypeScriptとJavaScriptのファイルが混在している場合
上記の二つのTypeScriptファイル
- file1.ts
- file2.ts
があるプロジェクトにJavaScriptのファイル
// JavaScript1.js "use strict"; function funcJS() { alert('funcJS'); }
が混じっているとする。
こういう状況は、多くのJavaScriptファイルがあるプロジェクトをTypeScript化する場合には良く起こり得る。
この場合には、funcJS()は何もしなくても定義した時点でグローバルな関数として定義されているので、他のJavaScriptファイル、TypeScriptファイル内から参照出来る。
declare宣言を不注意に追加すると泥沼にはまる
ただし、TypeScriptファイルからJavaScriptファイルの関数を参照する場合には、declareという宣言文が必要になる。
// file2.ts "use strict"; declare function funcJS(); // この宣言文が必要 module mod2 { window.onload = () => { console.log("ONLOAD"); funcJS(); } }
もしdeclare宣言を忘れると、
となる。
ところが、
大量のJavaScriptファイル群をTypeScriptファイル化している最中には、この
と言う形式のエラーも大量に出るので、そのエラーが出たら兎に角
declare function funcXXX();
をTypeScriptファイル冒頭に挿入する癖が付いてしまう。
そこが落とし穴だ。
エラー Cannot find name ‘funcXXX’ の理由は一つでは無い
関数が見つからない原因としては、
- declare宣言が不足している場合
- 他のmodule内にある関数でexportが付いていない場合
- その他(の場合もあるのかな?)
などである。
declare宣言が不足している場合なら、このdeclare宣言を追加すると解決する。
一方、他のmodule内にありexportが付いていない関数の場合にはこのdeclare行を追加してもエラー解消の効果が無い事は言うまでも無いのだが、即席で追加したそのdeclare宣言行自体はエラーでも何もない正常な構文なのだ。
つまり、そのdeclare宣言文が有る事によって、どこかよそのtsファイルやjsファイルで
function funcXXX(){ ... }
と言う関数が定義されていますよとTypeScriptに教える事になる。
問題はここからだ。
実際に存在しているmodule mod2内のfuncXXXにexportを付ければそれで
自体は解消するのだが、それとは別に、うっかり追加してしまった
declare function funcXXX();
の行があると、TypeScriptコンパイラは混乱するようだ。
Overload signatures must all be ambient or non-ambient in TypeScriptが出る
その結果、タイトルに記載したエラーメッセージ、
が出るのだ。
Overloadと出ているので、同じ関数名 funcXXX()が二ヶ所にあると解釈されているようだ。
なので闇雲にdeclare宣言文を追加すれば良いと言う訳ではない。
ちなみに、このdeclareキーワードを用いて、関数や変数などのオブジェクトに対して、
declare function funcXXX(); declare var my_name: string;
のような宣言を行う事をアンビエント宣言と言うらしい。
う~ん、何でアンビエントと言うのかは未確認だ。
declareで宣言しているなら、例えばディクレアー宣言と呼ぶ方が分かり易いと思うのだが。
何で態々ディクレアーをアンビエントと言い換えるのかな。
何か政治的な意図でもあるのか?
- 敗戦 ➜ 終戦
- 全滅 ➜ 玉砕
- 自爆攻撃 ➜ 特攻
- 放射能漏れ事故 ➜ 放射能漏れの事象
- 税金 ➜ 公的資金
などいくらでもある。
まあ、TypeScriptと政治問題の関係は未確認だ。
それに興味ないし。
なんか良く分からん音楽だ。
まさにアンビエント‼
Visual Studio を再起動すると治る場合もある
なお、Visual Studioでこれらの作業を行っていると、エラー箇所を正しく修正しているにもかかわらずエラーメッセージが消えない場合がある。
そういう場合には、一旦そのVisual Studioを閉じて再起動すると正常に動作する場合がある。
どうやらVisual StudioのTypeScriptコンパイラさんは、今一つ完成度が高くない感じ。
編集中にIDEの画面が数秒間固まるなどの症状もたまに出る。ワテの場合メモリは32GBと十分積んでいるのでメモリ不足などではない感じ。
どちらかと言うと、エラーが多数あるとTypeScriptコンパイラが右往左往して固まっている感じだ。
なので、そういう場合にはVisual Studioの再起動で直るなどという原始的な手法が有効なのだと思う。
ちなみに、Visual StudioでTypeScriptが利用できるようになったのが、2014年4月3日の開発者向けイベント「Build 2014」であり、そこでバージョン1.0のリリースが発表された。
だから、まだ約二年しか経っていないのか。
TypeScript Definitions (d.ts)ファイル
ちなみに、このdeclare宣言は、ワテの場合はfile1.tsやfile2.tsの冒頭に直接追加したが、一般には、別ファイルでTypeScript Definitions (d.ts)として定義するのが普通なのかな。
例えば、jQueryをTypeScriptで使う場合には、
jquery.TypeScript.DefinitelyTyped
をNugetでプロジェクトにインストールすると
jquery.d.ts
というファイルが追加される。
そのファイルを file1.tsにドラッグすると自動的に先頭に以下の一行が追加される。
/// <reference path="scripts/typings/jquery/jquery.d.ts" /> // file1.ts "use strict"; module mod1 { ... }
TypeScript Definitions (d.ts) for jquery というファイルだ。
これでjQueryをTypeScript内で利用できるようになる。
まとめ
JavaScriptファイルが大量にあるVisual StudioプロジェクトをTypeScript化する場合には注意が必要。
関数funcXXX()が見つからないと言うエラーが出た場合に、闇雲に
declare function funcXXX() ;
を追加すると、
というエラーが出るので注意。
関数が見つからない理由には、declare宣言文を忘れている場合もあるが、それ以外に単にexportを付け忘れているだけの場合もあるからだ。
後者の場合にもかかわらず、declare宣言文を追加すると、その関数が別のファイルで定義されているとTypeScriptコンパイラが誤認して、このエラーメッセージが出るようだ。
JavaScript・TypeScriptの本を読む
どこか変なん?
著者は有名な川俣晶さんだ。
Amazonで
を見る。
コメント