JavascriptはC#と違ってバグに気づきにくいです。

たとえばこんなコード。

func1関数はnumber型を返します。これに対してfunc2関数はstring型を返します。そのため以下のふたつは異なる結果になります。

また以下のようなコードを書いてもエラーになりません。

関数の場合

このようなミスは実行時にならないとバグと気づくことができません。C#のようにコーディングのときに気づくことはできないのでしょうか? 実は //@ts-check を使うことでバグに気づきやすくなります。

//@ts-check は先頭に書きます。途中に書いても機能しません。

関数の前に

と書くことで、引数のnumはnumber型、戻り値もnumber型と指定することができます。なのでfunc1関数にnumber型ではないものを渡すとすぐに赤線がつきエラーであると気づくことができます。

このようなJSDocをつけることで関数の引数と戻り値の型をわかりやすくすることができます。ただし以下のような書き方では機能しません。/ のあとに * をふたつ以上つけなければなりません。

複数の引数がある場合は@paramの行を増やせばOKです。

ただし以下のように同じ名前の関数を複数回定義した場合はエラーを指摘してくれません。console.log(func4(1))をどこで実行しても最後に定義された部分がこの関数の定義になります。

変数の場合

変数の場合は以下のようにします。変数 d はstring型です。ところがfunc1関数はNumber型を返すので間違いに気づくことができます。

クラスの場合

クラスの場合はどうなるのでしょうか? いろいろ突っ込みどころ満載のクラスを定義してみました。

同じ名前の関数がふたつあります。この場合は赤線で指摘してくれます。ただし実行は可能でその場合は最後に宣言した関数が適用されます。

Func2関数は引数がnumber型ですが、2回目の呼び出しではstring型を渡しています。この場合は’abc’に赤線がつき、引数の型が間違っていることを指摘してくれます。ただ実行しようとするとエラーにならず実行されてしまいます。これがJavaScriptの怖いところです。

Func3関数は引数がstring型ですが、これをnumber型のXに代入しようとしています。この場合はFunc3関数内のthis.Xに赤線がつき、引数と型があっていないことを指摘してくれます。ただこの場合も実行しようとするとエラーにならず実行されてしまいます。

Yは/** @type {} */ がありませんが、0で初期化されています。だからnumber型です。なので

と文字列を代入しようとすると赤線がつきます。ただしこの場合も実行可能で xyz と出力されます。

abcdeを書き換えようとしてタイプミスをしてしまったとします。これがC#ならそんなメンバーはいないとエラーが出るところですが、JavaScriptはエラーになりません。そしてタイポに気がつかず console.log(myClass.abcde);を実行。100 が出力されることを期待していたのに 0 と出力され、「値が0から100に変更されないのはなぜだ?」とバグ探しに無駄に時間を費やすことになるのです。

しかし//@ts-checkとしていれば、abcedの部分に赤線がつくので気づくことができます。

number型の配列と思っていたら…

数字をカンマ区切りでひとつの文字列にして関数に渡して、内部で配列に分解して処理をしたいときがあります。以下のコードは配列の全部の要素から1を引いたものを取得しようとしています。[ 0, 1, 2, 3, 4 ]と出力されることを期待していて、実際に[ 0, 1, 2, 3, 4 ]と出力されます。

では以下の場合はどうでしょうか?

これは配列の全部の要素に1を加えたものを取得しようとしています。[ 2, 3, 4, 5, 6 ]と出力されることを期待しているのですが、実際にはそうはならず[ “11”, “21”, “31”, “41”, “51” ]となってしまいます。console.log(ar3)とやれば出力された要素が””で囲まれているので「しまった!文字列だった」と気づくことができますが、ar1[i]をさらにnumber型に代入したり引数として渡す場合はわかりにくいバグになります。

そこで以下のように書けばar3はnumber型の配列であると明確に定義することができ、ar3.push(ar1[i] + 1);と書いたときにar1[i]の部分に赤線がつき、すぐに間違いに気づくことができます。

canvasを使った描画

//@ts-checkとJSDocを書いておくと型チェックができるだけでなくコード補完の機能も強化されるのでおすすめです。タイピングが苦手な方はぜひ先頭に//@ts-checkを書きましょう。

ただcanvasを使った描画をするとき、このコード補完の機能がうまく働いてくれません。

このあとcon.とタイピングしたときに候補になりそうな関数が表示されてくれると助かるのですが、なぜかうまくいきません。

これだと赤線がつきます。/** @type {HTMLElement} */ ではなく /** @type {HTMLElement | null} */ と書けといわれたり、getContextはHTMLElementには存在しないとかいわれます。

getContextが存在するのはHTMLElementではなくHTMLCanvasElementです。しかし/** @type {HTMLCanvasElement} */とすると赤線がつくのでここは仕方なく以下のように// @ts-ignoreで黙らせます。これで入力補完でgetElementByIdが表示され、con.と入力すると呼び出し可能な関数が表示されます。

あとは描画のための処理を記述します。

複数のファイルにまたがった場合も型チェックやエラーチェックが可能

複数のJavaScriptを読み込むときにも対応させるには、以下のようにします。同じディレクトリにfoo.jsがある場合はこれで入力補完にfoo.jsで定義した変数や関数が表示されます。

foo.js

bar.js