プログラミング初学者向け TypeScriptのメリット・デメリット・なぜ使うのかを分かりやすく説明してみる

TypeScriptとJavaScript

以下のJavaScriptコードを見てください。

const add = (a, b) => {
    return a + b
}

二つの値を足して合計値を返す非常にシンプルな関数です。

この関数を見た人のほとんどは、引数のaとbが数値で渡されることを予想できると思います。

そのため、以下のように実行するでしょう。

add(1, 2) // 3

しかし、誤って文字列を渡してしまった場合はエラーにならなりませんが想定しない結果が返ります。

add(1, '2') // 12

TypeScriptでは、このような想定していない型を割り当ててしまった場合に、実行する前に気づくことができます

const add = (a: number, b: number) => {
    return a + b
}

add(1, 2)
add(1, '2') // 型エラー

この実行する前にというのはどういうことでしょうか。

TypeScriptとJavaScriptの関係性

ご存じの通り、JavaScriptのコードというのはブラウザに読み取られて実行されます。

しかし、TypeScriptのコードは直接はブラウザに読み取られません。

TypeScriptのコードはJavaScriptに変換されてからブラウザに読み取られます。

TypeScript —> JavaScript —> ブラウザ

JavaScriptのコードはいきなりブラウザに読み取られて実行されるので、先ほどのように想定しない結果が発生するのは、実際に実行した時になります。

一方で、TypeScriptの場合は、TypeScriptからJavaScriptへの変換の過程で、型が正しいかをチェックします

つまり、先ほどのadd関数に文字列を渡したコードがあった場合、この変換時にエラーを検知できます。

TypeScript –(ここで型のエラーを検知)–> JavaScript —> ブラウザ

そのため、ブラウザがコードを実行する前に検知できます。

そして、VSCodeなどのエディタを使っている場合、エディタ上で型エラーがある部分が赤線でわかるようになっているので、開発中に間違いに気が付くことができます。

これによって、あなたの書いたコードが本番環境で実行された時にエラーになりクライアントから怒られる、ということを回避できます。

TypeScriptのメリット

これまで述べたように、TypeScriptを使うと事前に型のエラーを検知して、プログラムに不正なところがあることを検知できます。

他のメリットして、ドキュメントとして活用できます。

以下のコードを見てください。

const getFullName = (user) => {
    // 何かの処理
}

この関数の引数のuserは、どのようなデータか分かるでしょうか。

勘が良い人はわかるかもしれませんが、この状態だとわかりません。

ではこれだとどうでしょうか。

const getFullName = (user) => {
    return user.firstName + ' ' + user.lastName
}

関数内の実装が見えたことで、おそらく user というのはオブジェクトであり、firstName と lastName というプロパティが必要であることが予測できます。

このように、JavaScriptの場合は実装を読まないと引数がどのようなデータか判断できません。

この関数はたった一行のコードですが、例えば何十行にも及ぶ関数だったらどうでしょうか。

もしくは、関数内で別の関数を呼び、さらにそこから別の関数を呼ぶ、、、といった場合は、いろいろなファイルに飛んですべての実装を読まなくてはいけません。

この問題に対して、コメントを書くという方法もありますが、コメントを書くことは良い方法とは言えません。

というのも、もしgetFullNameの実装が変わって引数のデータ構造が変わった場合には、同時にコメントを変える必要がありますが、それを忘れる可能性があるからです。

もし、忘れた場合はコメントは嘘の情報を言っていることになり、開発者を混乱させてしまうので逆に大きなデメリットになります。

ではTypeScriptで書いてみます。

// ①
type User = {
    firstName: string
    lastName: string
}

const getFullName = (user: User) => { // ②
    return user.firstName + ' ' + user.lastName
}

分かりやすいように User という型をまず定義しています。

User は、firstName と lastName というプロパティをもち、どちらも string 型であることが分かります。(①)

そして、getFullName の引数の user に、User 型を割り当てています。(②)

このようにすることで、関数内の実装を読まなくても、開発者は getFullName 関数の引数がどのようなデータなのかを知ることができます。

コメントと違うところは、もし実装が変わった場合には、型の定義も同時に直さないと型エラーになるので、気づくことができる点です。

type User = {
    firstName: string
    lastName: string
}

const getFullName = (user: User) => {
    return user.fName + ' ' + user.lName // 型エラー
}

firstName が fName へ、lastName が lName へ変更になりましたが、もし上記のように User の型定義をそのままにした場合は、型エラーが検知されます。

このように安全なドキュメントとしても役立てることができます。

TypeScriptのデメリット

それではTypeScriptのデメリットは何でしょうか。

大きく二つあると思っていて、一つ目は学習コストが高いということです。

プログラミングを始めたばかりで、なんとなくJavaScriptが分かってきたなという方にとってはなかなか理解が厳しい部分があると思います。

もう一つは、すでにお分かりだと思いますが、コードが読みづらくなります。

型の情報がコードに付与されることで、実際の処理を読むときにノイズになります。

これは一長一短で仕方がないのですが、あまりに複雑な型の定義を実装の中に盛り込んでしまうとわけが分からなくなることがあります。

まとめ

現在はフロントエンドでTypeScriptを使うことが主流になりつつあります。

それはフロントエンドで複雑な処理を行うようになってきたためです。

TypeScriptを学ぶことで他の言語の理解の助けにもなると思うので、将来を見据えて学習することをお勧めします。