非エンジニアの社長にTypeScriptとは何か説明する
以前ReactでやったシリーズのTypescript編です。
現在進めているProjectではTypeScriptを利用しているのですが、導入時に若干揉めました。
JavaScriptはともかくTypeScriptを業務レベルで書いたことのあるメンバーがほぼいなかったためです。
社長には「長期的に利用するコードだったらこっちの方がいいんだ」とだけ説明して、
CTO権限で押し切ってしまったのですが、大分雑な説明だったので一度きちんとまとめておこうと思います。
そもそもTypeScriptとは何か?
プログラミング言語の一つで、TypeScriptで書いたコードにコンパイルという処理を加えると、javascriptのコードが生成されます。
一般にコンパイルというのはプログラムのソースコードから機械語などの人には厳しいものの機械が実行するには都合のいいものを生成することを指しますが、TypeScriptをコンパイルするとJavaScriptのコードが生成されます。
これはブラウザ上で画面を動かすために使えるものがほぼJavaScriptに限定されるためです。1
なぜ、TypeScriptで書く手間をかけるのか?
他のコンパイルが必要な言語の生成物違って、JavaScriptは元々人が読み書きするために作られた言語なので、当然JavaScriptでコードを書くことは出来ますし、実際、直接JavaScriptで書かれて動いているサービスも世の中に沢山あります。
それにも関わらず、なぜわざわざコンパイルという手間と学習コスト2までかけてTypeScriptで書くのか。
これは、TypeScriptを使うことで静的型付けというメリットが得られるためです。
静的型付けとは何か?
そもそも型とは?
およそプログラムと言うものは、どこかから値を取ってきて、
そこに何らかの処理や計算を加えてどこかしらに結果を出力するものです。
(例えば、このQiitaの記事を読む場合、記事のIDから記事の情報をデータベースに取りに行って、
格納されているデータをHTMLの形に加工して出力しています。)
この時に扱う値の種類が「型」です。
「文字」とか「数値」、「日付」であったり、あるいはユーザーが定義したもっと複雑な型(申し込みデータなど)もあります。
プログラムはそれぞれの値が何の型であるかによって処理ができるか判断したり、処理を切り替えたりします。
//javascriptでの具体例
3 + 3 // この結果は6。型が「数値」なので普通に足し算します。
'3' + '3' //この結果は'33'。シングルクォートで囲まれた数値は「文字列」なので、+は足し算ではなく文字としてくっつけます。
静的型付けと動的型付け
プログラムにおける型の扱いには大き分けてく2つあります。
静的型付けと動的型付けです。
前者は、コードを書く時点でその値に「特定の型でなくてはならない」という制限を設けておくもので、
そのルールに反したコードを書くとコンパイルの時点でエラーが発生します。
後者は「動的」の名の通り実行時にその都度判断します。
実際に実行してみて問題が発生しない限りエラーは起きません。
//typescriptの場合
var a:number = 1;///変数aの型はnumber(数値 )
a = 'aaa';//aの値を文字列にしようとするとエラーが起こってコンパイルできない
//javascriptの場合
var b = 1; ///javascriptはコード上で型宣言をしない
b = 'bbb'; ///型宣言がないのでbには何を代入してもいい
生のjavascriptは動的型付け言語で、typescriptは静的型付け言語に当たります。
静的型付けだと何がうれしいのか
なお、静的型付けと動的型付け一般について語ると広くなりすぎて色々と例外も出てくるので、
静的型付けといいつつ、TypeScriptとJavaScriptの比較を想定した文脈で語ります。悪しからず。
不具合の早期発見
静的型付けの良いところは、バグの一部を早い段階で発見できることです。
例えば、二つの料金の合計額を求めて、消費税をかける処理があるとします。3
javascriptで書くと以下のような処理です。
function calc (priceA,priceB){
return (priceA + priceB) * 1.08;
}
これは一見問題なさそうに見えます。実際、100
と100
を渡すときちんと216
が返ります。
が、数字の100
を渡すつもりで間違えて文字列の'100'
を二つ上記の関数に渡してしまうとどうなるでしょうか。
なんと、数値の108108
が返ります。金額が500倍以上になる非常にまずい状態です。
これは文字列の'100'
同士を+すると文字列として結合されて'100100'
になり、そこに1.08
をかけようとすると、
javascriptの処理系が「掛け算するってことはきっと数値として扱いたいんだな」と要らない空気を読んで
'100100'
を数値に変換(暗黙型変換)した上で1.08倍するためです。
このように、実際の処理と言うものは特定の型でない限り、まともに動かないものがほとんどです。
では上記をTypeScriptで書くとどうなるでしょうか?
function calc (priceA:number,priceB:number){
return (priceA + priceB) * 1.08;
}
TypeScriptの場合、足し合わせる二つの値に「数値型でなくてはならない」という制限を設けることが出来ます。
この制限があるため、文字列の'100'
を渡そうとすると、「数値ではないからダメです。」とエラーを出してくれます。
実際に処理を回さなくても、コードを書く時点でバグに気づいて修正できるわけです。
(最近のエディタは賢いので、わざわざコンパイルを回さなくてもこういうレベルのエラーは書いた瞬間に指摘してくれます)
早い段階でエラーが検出できるというのは、開発効率に大いに寄与します。
というのも、バグの修正コストは発見されるフェーズが後になればなるほど指数関数的に修正コストが増すからです。
(書いている時に気づけば一瞬ですが、本番環境に当たった後だとそう簡単に修正できませんし、データ復旧などの面倒な付随作業が必要になる可能性があります)
他にも、色々な場所から参照されるロジックを修正した結果想定外の所が動かなくなるバグ(影響範囲の見落とし)など、静的型付けによるチェックで防げるバグは多くあります。
小規模なプログラムの場合は、それぞれの処理で要求される型を覚えていられたり、影響範囲を完全に把握したりできますが、大規模開発や長期間に渡ってメンテナンスしていくコードになるとまず無理です。
型によって事前にチェックしてもらった方が安心です。
エディタによるアシストの強化
プログラマはコードを書くのが仕事ですが、その生成物のコードが全て手で打たれたものかと言うとそんなことはありません。
プログラマは怠慢な生き物なので可能な限りエディタに補完してもらいます。
(いちいち手打ちしていたら腱鞘炎になってしまいますし、タイポばっかりでいつまで経ってもコードが完成しません。)
そして、このコードアシストですが、型情報があった方が確実によく働きます。
例えば、先ほど例として挙げた金額を合計する処理ですが、JavasSriptの場合だと処理に必要な要素として何をサジェストするのが適当かを判断する材料がエディタ側にはほどんどありません。
仕方ないので使えそうなものを全て提示する形になり、当然その中には実際には使えない別の型の値も混じってしまいます。
一方でTypeScriptの場合、型が決まっているので実際に使えるものに絞り込んだ上でサジェストできます。
がっつり開発する場合は型情報とそれを利用した適切なアシストがあったほうが楽です。
型がある方がコードが読みやすい
大規模な開発になると人の書いたコードを読んで仕様を理解したり、外部のライブラリを利用することが増えます。
この時、型があると内容を理解するのが楽になります。
例えば、何らかの申し込み処理がある場合、JavaScriptの場合どのような項目が必要になるのか値の名前でしかわかりません。
それぞれの値をどのような型で渡せばいいのか、渡す値がなかった場合にどうすればいいのか、成功・失敗の場合それぞれどのように値が返ってくるのか、と言った開発者が欲しい情報はコード上には表現されません。
そのため、ドキュメントやコメントを参照する必要がありますし、最悪それらがなければコードを読んで仕様を解読する必要があります。
これは中々に骨が折れる作業です。
一方で、きちんと型定義がされていると、型情報を見るだけで仕様がほぼわかりますし、(尤も、雑に型定義されていると分からない場合もありますが…)
それに基づいて前述のコードアシストも働きます。これも開発効率の向上に寄与する要素です。
まとめ
- TypeScriptはバグの早期発見やコーディングの効率・可読性の向上に寄与する
- 一方で、上記のメリットはプロジェクトが大規模化・長期化するほど大きくなるもので常に最適解というわけではない