記事内に広告が含まれています

【ワテ備忘録】Overload signatures must all be ambient or non-ambient in TypeScript【解決】

この記事は約11分で読めます。
スポンサーリンク

Visual Studio 2015 Communityで自作のJavaScriptプログラムをTypeScriptに変換した。

その時に、沢山のエラーが出るのだが、

タイトルに示した、

 Overload signatures must all be ambient or non-ambient in 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宣言を忘れると、

エラー  Cannot find name ‘funcJS’.

となる。

 

ところが、

大量のJavaScriptファイル群をTypeScriptファイル化している最中には、この

エラー Cannot find name ‘funcXXX’.

と言う形式のエラーも大量に出るので、そのエラーが出たら兎に角

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を付ければそれで

エラー Cannot find name ‘funcXXX’.

自体は解消するのだが、それとは別に、うっかり追加してしまった

declare function funcXXX();

の行があると、TypeScriptコンパイラは混乱するようだ。

 Overload signatures must all be ambient or non-ambient in 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 1.8くらいだったが、最近ではTypeScript 3.1くらいに上がったので、原因不明のエラーも無く、非常に安定していて使い易い。(2019/01/22)

 

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() ;

を追加すると、

 Overload signatures must all be ambient or non-ambient in TypeScript

というエラーが出るので注意。

関数が見つからない理由には、declare宣言文を忘れている場合もあるが、それ以外に単にexportを付け忘れているだけの場合もあるからだ。

後者の場合にもかかわらず、declare宣言文を追加すると、その関数が別のファイルで定義されているとTypeScriptコンパイラが誤認して、このエラーメッセージが出るようだ。

JavaScript・TypeScriptの本を読む

どこか変なん?

著者は有名な川俣晶さんだ。

Amazonで

コンピュータ・IT の 売れ筋ランキング

を見る。

スポンサーリンク
コメント募集

この記事に関して何か質問とか補足など有りましたら、このページ下部にあるコメント欄からお知らせ下さい。

TypeScript
スポンサーリンク
シェアする
warekoをフォローする
スポンサーリンク

コメント