LoginSignup
76
53

More than 1 year has passed since last update.

銀の弾丸ではないTypeScriptを、何故使うべきなのか

Last updated at Posted at 2021-09-19

<まず簡単に>  TypeScript とは

image.png

TypeScript は、JavaScript でも大規模なアプリケーションを開発しやすくすることを目的に開発されたプログラミング言語である。
JavaScript の上位互換であり、JavaScript の文法のまま型などの様々な恩恵を受けることができる。
トランスパイル 1 することで JavaScript に変換され、これまで通りのブラウザや既存の環境でも動作させることができる。

<技術革新の影に政治的対立> TypeScript の登場背景

ちょっと長い、TypeScriptの登場背景

JavaScript がニーズに応じて進化していれば、TypeScript は必要なかった

そもそも JavaScript は、1990 年代に HTML の補助的な言語として誕生した。

大規模なアプリケーションを作るための言語とは見なされていなかったが、徐々に大規模開発にも耐えうる JavaScript のニーズが高まり、1999 年に新たな言語仕様である「ECMAScript 4」の検討が始まった。

以下が、その頃検討されていた「ECMAScript 4」(略 : ES4)の仕様である。

- モジュール
- 静的な型付け
- Nullable型
- ユニオン型
- ジェネリクス

これらは TypeScript で実現済みの仕様であるが、一方JavaScriptでは一部2021年現在も実現されていない仕様である。
すなわち、これらが実現していれば、TypeScript は必要なかった。
しかし、結論からいうと、ES4 は実現しなかった。
ここにTypeScript が生まれた理由がある。

政治的な対立

ES4 はこれまでの JavaScript と互換性がなく、革新的過ぎたため、保守派から大きな反対が生まれた。

ES4の検討を進めてきたMicrosoft社 と Netscape 社との間で、保守的な立場を取る Microsoft 社と、革新的な変更を加えたい Netscape 社の対立が起こり、政治的な背景からも折り合いをつけられなかったために ES4 の草案は破棄された。

AltJS の登場

このような政治的対立の焼け野原の跡地で、偉大な天才達は 大きな犠牲を払わずにメリットを享受できる、1つの革新的なアイディア を生んだ。

JavaScript 本体のアップデートが進まないなら、JavaScript にコンパイルできる言語を作れば良いのでは?

コーディングは別の言語で行い、それを JavaScript にコンパイルするという手法は成功し、このアプローチを採用する言語が数多く開発された。
それらは総称して AltJS と呼ばれるようになった。

そのうちの 1 つであった TypeScript は、

- JavaScriptと比較して破壊的な変更が無いために、既存のJavaScript資産をそのまま流用できた
- 莫大な学習コストを掛けることなく、安全な静的型付けを導入できた
- 既存にそのまま導入でき、徐々にTypeScriptの恩恵を増やしていける、という親切設計になっていた

ため、世界中で導入されるようになった。

その後「ECMAScript 2015」が発表されるなど、少しずつ JavaScript も大規模開発のハードルを下げるべく年々進化を続けているが、まだまだ TypeScript の優位性は揺らいでいない。

参考:

https://book.yyts.org/

<ホントしんどい> 改めて、JavaScript のツラミ

  • 型がゆるゆるで、めちゃくちゃなコードを書ける
  • だがしかし実行時エラーが発生しない
  • そのため、デバッグが難しい
5 == "5"; // true, TSはエラー
5 === "5"; // false, TSはエラー

[] + []; // JS は "" を返す (意味不明)。 TS はエラー
{} + []; // JS : 0, TSはエラー
[] + {}; // JS : "[object Object]", TSはエラー
{} + {}; // JS : ブラウザによって、NaN または [object Object][object Object], TSはエラー
"こんにちは" - 1; // JS : NaN, TSはエラー

function add(a, b) {
  return;
  a + b; // JS : undefined, TSはエラー '到達不可能なコードが検知されました'
}
  • class を書こうと思ったら書けるが、伝わりづらい/可読性が低い
  • 且つ JavaScript のゆるゆる this 仕様により、思わぬバグを生む
// ES6以前だとこう
var Point = (function () {
  function Point(x, y) {
    this.x = x;
    this.y = y;
  }
  Point.prototype.add = function (point) {
    return new Point(this.x + point.x, this.y + point.y);
  };
  return Point;
})();

// ES6以降だとこう
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  add(x, y) {
    return {x:this.x + x, y:this.y + y};
  }
}

// TSで書くとこう
class Point {
  x: number;
  y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
  add(point: Point) {
    return new Point(this.x + point.x, this.y + point.y);
  }
}

var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
var p3 = p1.add(p2); // {x:10,y:30}
// ↑ オブジェクト指向言語らしい、伝わりやすいクラス定義

<何を解決できた?> TypeScript の特徴

柔軟 な静的型付け言語

破壊的な変更を避けるという TypeScript の理念故に、柔軟な型付けとなっている
そのため、明示的な型を付けずとも TypeScript は動かせる

型を利用するかどうかも、型定義にどれだけの労力をかけるも、開発者の自由となっている

😇😇 「型によるメリット」単体についての詳細は、こちら😇😇

型によるメリット

  • 型推論によるバグの予防
    • コーディング時点でエラーに気付くことができる
    • ランタイム(実行時)で初めてエラーに気づいてコードに戻って修正するよりも効率的
  • 実行前に型を宣言するため、動作が早い

暗黙的な型推論

ソースコードを解析し、そのコードの流れから、変数や関数などの型を推測してくれる仕組み

var hoge = 123;
hoge = "456"; // TSエラー: `string` を `number` に代入できません

明示的な型指定(型アノテーション)

var hoge: number = 123;
var hoge: number = "123"; // TSエラー: `string` を `number` に代入できません

Generics

型を抽象化し、実際に使用されるまで型が確定しないクラスや関数を扱う型引数のことで、同じような機能を持っている複数の関数を一つにまとめ、コードの冗長化を省く

function f<T>(args: T) {
  console.log(args);
}

f<string>("hello"); // "hello"
f<number>(123); // 123

クラスを使ってオブジェクト指向開発ができる

  • 上述のように JavaScript もクラスは書けるが、他の言語学習者からすると取っ付きにくい書き方である
  • TypeScript ではクラスを公式にサポートしているので、JavaScript では冗長になりがちなコードの記述を大幅に簡略化できる

優秀なコンパイラ

  • ビルドオプションを変更することで、生成される JavaScript のバージョンを自由に変更することができる
  • 古い JavaScript(ES5 以前)の実行環境(IE9 や Node.js など)でも、ES6 以降のバージョンの機能を使うことができる
  • ブラウザで実行されるのは JavaScript であっても、ブラウザは TypeScript でトレースされたマップファイルを読み込む
    • そのため、ブラウザの開発ツールでデバッグを行うと TypeScript で表示されるので、デバッグがしやすい
  • コンパイラオプションで、null や undefined である可能性を持った変数を事前に検知できる
    • そのため、JavaScript で開発中に多くのエンジニアが直面する「Uncaught TypeError: Cannot read property *** of undefined」を事前に避けることができる

生きるドキュメントとしての価値

  • 型定義は簡易的な仕様書とも言えるので、ドキュメント整備が追いついていない現場や、不特定多数が絡む大規模・長期間のプロジジェクトで効果的
  • 定義クエリによって、コードと処理を照会する工数を少なくすることができる
  • それによって、リファクタリング時は JavaScript と比較して低コストで安全に開発することができる

高機能 IDE(統合開発環境)による圧巻のサポート

VSCode や JetBrains IntelliJ などの IDE を用いた TypeScript の開発は、エンジニアの開発体験を著しく向上させる

  • 強力なサジェスト機能
  • エディタにエラーチェックを任せられる
    • 神経質にエラーに気を配ること無く、コーディング中にエラーに気づくことができる
  • どんなデータを渡せばよいか一目瞭然

<ここで本題> とはいえ、銀の弾丸ではない

JavaScript や他の言語との差分

他の言語であれば特に気にならない「言語のクセ」も、「TypeScript が JavaScript のスーパーセット(上位互換)である」という謳い文句をそのまま受け取ると面食らうことがあるかもしれない

  • JavaScript を熟知したエンジニアからすると..
    • 静的型の制限があることによって、JavaScript では簡単に実現できていた設計が TypeScript では難しくなる、というパターンが起こり得る
    • そうなると設計モデルの一部を放棄するか、または多大な労力を費やして複雑極まりない型宣言を記述するかの選択を迫られることになり得る
  • 他の静的言語(Java、C#、Haskell など)の使用経験があるエンジニアからすると...
    • TypeScript の独特な型システムに困惑し得る

依存しているライブラリの安全性

  • 既存のシステムが、保守が不安定なライブラリを使っていた場合、TypeScriptに乗り換えたことで動かなくなるということも起こり得る
  • ライブラリの型定義が悪く想定した型が当たらないなどの事象が起こると、使用しているライブラリを変更するなどの対応が別途必要になる

開発体験

  • 静的型付け言語での開発に慣れていないチームの場合、最も厳密なコンパイルオプション下での開発は大変困難
  • 現在のコンパイラの処理速度は十分ではない
    • プログラムを変更してから再テストが可能となるまでには一定の時間待つ必要があるために、開発体験の低下は否めない
    • TypeScript を使用した有名プロジェクトである Deno でも、コンパイル時間が長すぎるなどの関連問題のために、一部の TypeScript プログラムが JavaScript 仕様に変更されたという経緯がある

コードの冗長性

  • 型宣言の占めるスペースがプログラムロジック部分よりも多くなる場合がある

TypeScript の型の安全性

  • TypeScript 静的型チェックが完全に安全なものではない
  • any の使用または型変換なくしては解決不可能な問題も多々存在する

チーム開発での課題

  • リソースが有限である状況下では、最良の投資であるとは限らない
  • TypeScript での開発経験が十分ではない場合、TypeScript の導入コストを見誤り、逆効果となる可能性がある
  • 導入初期は緩い型付けでも問題ないが、プロダクトのスケールに合わせて徐々に厳密な型付けをしていく必要がある。しかし TypeScript の十分な経験を有するメンバーがチーム内にいないと「厳密さの段階を適切に変更していく」ことが難しい

参考

<総括> それでも使うべきか

総括すると、デメリットを遥かに上回るメリットがある
もちろん銀の弾丸ではないので、チームの習熟度やプロダクトのフェーズを見極めつつ、適切に導入しましょう🎉💪

開発体験に関する観点

優秀なIDEの使用を前提としてはいるが、TypeScriptでの開発体験は現時点のJavaScriptと比較すると圧倒的に高い

TypeScriptを取り巻くエコシステムの整備状況も、開発体験を上げている一端を担っている

不特定多数が絡む現場や、不具合が忌まれる大規模な開発現場との相性が良く、高い開発体験を維持したまま高品質なプロダクト開発を実現できる

コストに関する観点

学習コスト

  • JavaScript と同様の構文が使えるため、他言語と比較したときの学習コストは低いと言える
  • JavaScript として書き始め、少しずつ TypeScript を学んでいき、徐々に TypeScript の恩恵を最大化するようにコードを手直ししていくといったアプローチが可能
  • とはいえ、もちろん入門から習熟まではそれなりに時間を要する

導入コスト

  • TypeScript には JavaScript にはない数多くの機能があるが、どれも選択的に導入していくことができる
  • ただ必須の初期コストとして、tsconfig.json の設定方法やディレクトリ構成を整えるためのコストが必要
  • また、既存の JavaScript のコードで型がらみのエラーを吐きまくることが起こり得るので、その場合は any で握りつぶすなどの対応が別途必要

保守コスト

  • 不具合が混入するリスクを低く抑えられるので、保守コストを低く保つことができる
  • しかし、プリミティブ型やその配列なら問題ないが、複雑なクラスや関数に正しく型を付けていくのはそれなりに力量が必要なため、開発コストは少なからず上がる

依存しているライブラリの安全性に関する観点

上記で指摘された「依存しているライブラリの安全性」については、基本的に有名どころの JavaScript ライブラリの型定義は、ほぼ全てDefinitelyTyped コミュニティによって既に作成・保守されているため、比較的安心して開発できる

とはいえ、第三者によって提供される型は必ずしも正確ではなく、また元のライブラリのバージョンがアップデートされたにも関わらず必要な型定義はアップデートされていない、という状況も起こり得る

その場合は以下のような対応を取る

- 自前で型定義を書く
- どうしようもない場合は any でエラーを潰す
- 当該ライブラリを仕様を避け、正しい型定義がある代替ライブラリに乗り換える

将来的なTypeScriptのポジション的な観点

JavaScriptのツラミを革命的に解決する言語が現れない限りは、今後もファーストチョイスとして選ばれ続けると考えられる

最近 Stackoverflow が発表したDeveloper Survey 2021を見ると、TypeScriptの人気は全体の5位に位置しており、人気の高さが伺える

image.png

また、Deno の登場にも垣間見えるように、TypeScriptは今後も世界中のエンジニアから重要な技術として認識されていくものと考えられる

参考

以下、素晴らしいドキュメントに感謝申し上げます。
大変に勉強になりました。

  1. トランスコンパイルの略。別の言語に変換すること。他の言語(C 言語や Java など)のコンパイルのようにマシン語や中間言語に変換しない。

76
53
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
76
53