yamakenji blog

「プロを目指す人のためのTypeScript入門」読書感想

2022/06/01

こんにちは、@yamakenjiです。
「プロを目指す人のためのTypeScript入門」を読み終えたので、 自分の中で新しく学べたところや感想を書いていきます。

1章 イントロダクション

TypeScriptとはそもそも何なのかというお話。
TypeScriptはその名の通りJavaScriptに型を付与したもの。そのため、JavaScriptを利用している場面ではTypeScriptに置き換えることが可能。
JS実行時に発生しうる「実行エラー」を型付けすることでコンパイル段階で防ぎ、意図しない挙動を減らしてくれる。
また、型情報を参照できるため、コード自体がドキュメントとなり、IDEなどを用いることで入力補完にも役立つ。

ECMAScriptはJavaScriptのもう一つの呼び名で、現在は言語使用の文脈の意味合いが強い。
ESxxとバージョンということが多く、2009年の第5版であるES5から2016年の第6版であるES2015に命名が変更されている。
これ以降の年は、xx年のES20xxという呼び方になっている。
ES5からES2015は大型なアップデートだったこともあり、ブラウザが対応しておらず、 そのためのトランスパイラが普及したという背景が現在のネット記事とかでES2015が使われていることが多いんだなということがわかった。

2章 基本的な文法・基本的な型

変数宣言でletとconstを紹介しており、新しくコードを書くときにはvarを利用しないという意図が見える。
letも極力利用せず、可読性を高めろというのは確かにそうだなと思った。
逆に、letを利用する場合は再利用しますよという意図にもなるので、仕様の背景にある利用シーンに応じたコードの意図をうまく汲み取れるようになりたいと思った。

等価演算子で、「基本的に==と!=は利用するべきではない」とのこと。背景として、暗黙の型変換を行って比較するため、意図せずtrueになることがあるため。 ただし、nullを比較する場合は、「nullとundefined」の比較になり、値がないという比較を行いたい場合に利用できる。

短絡評価のもので、普段よく使いそうなもの

  • || は左辺が偽の時に右辺を返す (空文字や0, falseなども偽扱いとする)
  • && は左辺が真の時に右辺を返す (nullまたはundefinedの時のみ)

3章 オブジェクトの基本とオブジェクトの型

JSのオブジェクトの仕様についての解説。

オブジェクトをconstで宣言しても、中身は再代入が可能となる。 これは、constで宣言した変数に対して規制をかけているため、その中身であるプロパティは制限されていないから。 readonlyとか使えば、中身のプロパティは制限できるけど、ネストが深い時も考えないといけないなと思った。

型名を宣言する文として、typeとinterfaceがある。歴史的な背景としてtype文が存在しない時代もあったが、現代はtype文を使っておけば困ることはないそう。 明確に、interfaceとtype文の棲み分け的なものがどうなっているのか知っておきたいなと思った。

インデックスシグネチャが型安全性を破壊してしまう要素があり、便利だけどMapオブジェクトで代替しようねというのは初知り。
Map、わざわざコンストラクタ宣言しないといけないしめんどくさい印象。

typeofを使うときは、「何が最上位の事実か」を意識することが重要。
例として、値などが最上位に来る時にtypeofで型を抜き出して利用する。

const size = ['small', 'medium', 'large'] as const;
type Size = typeof size[number];

4章 TypeScriptの関数

関数を作る方法などの解説

色々あるが、アロー関数がやはり使い道が多いなという印象を受けた。 引数の型の部分型関係で、型が部分型だったら、使うことができるという点。雰囲気で掴んでいた箇所。
ジェネリクスの型引数の型を関数の引数から推論ができる。

5章 TypeScriptのクラス

クラスの解説
普段の実装ではクラスは使うことがほぼなく、Javaでのクラスのイメージしかなかったのでちょうどよかった。
インスタンスを生成するときに、コンストラクタに引数がなかったら()を省略できる

静的プロパティ・静的メソッドはインスタンスではなく、クラスそのものに属する。
TypeScriptでprivateを宣言する時には、private以外に#がある。 これも歴史的な背景で、元々JavaScriptにはprivate機能が存在せず、TypeScriptからprivateになり、JSにも#が導入されたそう。
違いとして、TSのprivateはコンパイル時のチェックしか行っておらず、JSの#ではランタイムでもみている。
instanceofは与えられたクラスオブジェクトのインスタンスを確認する
implementsはクラスに追加の型チェックを行う。与えられた型の部分型である宣言であり、型が満たしていなければコンパイルエラーが発生する。

thisは関数の呼び出し方によって何を指すのかが変わる。知らず知らずにアロー関数の恩恵を盛大に受けていたんだなと思った。
クラス内で定義したプロパティにメソッド内でfilterとかで比較したい時にthisを使うと、参照先がメソッドに向くため、thisを退避する必要があったことを知った。
現在はアロー関数があるため、意図しない限りは極力アロー関数の方がいいと思った。

6章 高度な型

ユニオン型とインターセクション型
オプショナルチェイニング時にたとえばuser?.isAdult()とかの場合に、userがundefinedでもエラーが発生しないということは初めて知った。 オプショナルチェイニングは、それ以降のアクセスをundefinedだった場合にまとめて飛ばすとのこと。

リテラル型で宣言したものは、letで入れると再代入が期待されるためにプリミティブ型に拡大される。
ユニオン型を使用するときは、型の絞り込みを行う必要があることは知っていたが、あえてtagプロパティを与えて確認する方法もあるということを知った。 確かに、タグがあると、ユニオン型の値を扱うときに楽だなと思った。
keyofでユニオン型にした後に、特定の型だけにしたい場合は&で指定する。

asによる型assersionはTSの型安全性を意図的に破壊するため、人間の責任のもと型の絞り込みを行うなど、限定的に使うなどの判断が必要となる。
逆に、as constは以下のような効果がある。

  • 配列リテラルの型推論をタブル型にする
  • オブジェクトリテラルから推測されるプロパティは全てreadonlyになる
  • string, number, BigInt, Booleanリテラルがwideningしなくなる
  • テンプレート文字列リテラル型になる

assertsも人間によって型の絞り込みを行い、型安全性を損なう機能。
ただし、人間が型の責任を保証するという点で、ユーザ定義型ガードの中で一番用いることが多い。
この辺りをうまく使いこなせていけば、TS力も向上したなと実感できそう。

よく使う{[P in K]: T} ってmapped typesって呼ぶのを知った。

7章 TypeScriptのモジュールシステム

defaultエクスポート・インポートは基本的に使わない派ですが、その説明が書かれていてすごく納得。
特に、階層が深くなった時とかに入力補完が効かないとしんどいなという印象。 そのためにも、変数名・関数名はユニークになるようにつけることが重要。

型定義ファイルの作り方も紹介されており、ライブラリの型を自分で書く必要がある場合などに重宝しそう。

8章 非同期処理

JSがシングルスレッドモデルを採用しており、時間がかかる処理がきたらブロッキングされるため待ち時間が発生する。
非同期に処理を行って、その待ち時間の処理時間を有効活用しようという背景があっていいなと思った。
非同期処理を扱うためのコールバック関数が使われていて、巷によくきくコールバック関数地獄がこのことかーと読んでいた。
ES2015でPromiseが導入されてから、Promiseオブジェクトを利用することで非同期処理を扱えるようになった。
さらにもっと便利に扱えるようにしようということで、async/awaitが出てきたという紹介の流れがよかった。 新しく学習し始めた時にはすでにasync/awaitが導入されており、Promiseと混合することが多かったため、 そういう関係にあったのかという歴史的背景が学べるため、もっと早くこの本を読みたかったと思った。

9章 TypeScriptのコンパイラオプション

TSのコンパイラオプションよくわからないのが多いので、参考になった。 新規開発とかには一番厳しくしたほうが後々幸せになることや、逆に途中でTSを入れる場合は徐々にきつくしていったほうが挫折しなさそう。

感想

そもそもJSとはから始まり、TSの表現を抑えていきながら思想というかより良い実装も紹介されていて、すごく学びが多かった。 特に「TSが責務を持つ型を壊すときは、ユーザがそのプログラムに責務を持つ」はなるほどその通りだなと思った。

定期的に振り返って、TS力を向上していきたい。