積み上げ消化:プログラミングTypeScript第1~3章

 学習した技術書「オライリー社 プログラミングTypeScript」の学習した内容。

 学習記録を兼ねた備忘録。

 

 第1章 イントロダクション

 型安全性とエラー発見タイミング

 第一章はイントロダクションに過ぎず、内容はほとんどない。

 読んでみたところ、TypeScriptの恩恵はひとまず、型安全性エラー発見タイミングに尽きる。

 

 型安全性とは、「型を使ってプログラムが不正なことをしないように防ぐこと」、と書いてある。

 4章まで学習した結果の自分の解釈はこうだ。

 JSにはそもそも暗黙的な型変換があり、属するクラスや型によってプロトタイプも継承する。自由度こそ高いが、ゆえにエラーも起きやすい。

 不正な処理を興さないように、変数に明示的に型という通行手形を与え、処理の際にこの通行手形を確認するというもの、だと思っている。

 例えば本文では文字列と数値の加算演算子の処理を例に挙げている。普通せんやろと。なるほど確かに。
 わざわざ型の違う値同士を使用する処理なんてないだろと。そういう処理をルールも設けずにやっていると、アプリが肥大化するにつれてレガシーコードになっていくのを防げる…のがこの基本の恩恵なんだと思う。

 

 エラー発見タイミングも大事だ。エディタでコードを書いている最中にエラーが発見できるのは大きい。

 さらにはエラーには型の情報も出るため、なぜそれが不正(と判別された)処理なのかもわかりやすい。

 エディタとブラウザコンソール画面を行き来しなくて済む。

 

 第2章 全体像

 型チェッカーと型システム

 実際のコードはTypeScriptコンパイラーによっていったん空白などが取り除かれるが、この際に型チェッカーによるチェックが入る。つまりは実際に実行前に型チェックがされるというわけ。

 型チェックは実際の挙動には影響しないため安全なのもポイント。
 本文ではより詳細にコンパイラーによってソースコードがASTに解析され、それがバイトコードコンパイルされ、ランタイムによって評価されることで初めて結果が得られる…という工程があるらしい。

 TSコンパイラーが更に一枚かむ…というわけだけどそこはいったんおいておこう。

 大事なのは、実行前にチェックができること、さらには型はチェックの際にしか作用しないため安全ということ

 

 型のシステムには2種あり、アノテーションによって明示的に宣言するものと、システムによって自動的に推論させるもの

 後者は必然的に通常のJSと構文が変わらないため割愛。

 アノテーションは「値 : 型」という構文で値=変数に型を与えるというもの。

 例えばnameという変数に文字列の型を着けたい場合、let name : string = ...となる。プロパティの記述みたいな。

 アッパーキャメルケース記述だとJSのクラスになるので注意。

 すべての値に型の保証がなくてもコンパイルは可能。だが明示的であれ推論させるのであれ型保障は100%を目指すべき、とのこと。

 アノテーションによる明示的な宣言はどこで使うべきか。

 基本的には推論で良いけど、大事なところで使う事。同様に型変換も明示的にやったほうが良いとも書いてある。

 

 第3章 型について

 型について

 型とは、値とそれを使ってできる事柄の集まり

 例えば数値……5があったとして、それを加算したり、数値クラスのメソッドの使用ができる。これが型。

 値そのものだけでなく、それが何をできるのか(何kができないのか=してはいけないのか)も重要

 値を使って不正な処理をしないようにするという型安全性を思い出すと納得だ。

 数値を受け取り二乗した値を返す関数があったとして、パラメータに数値型がアノテートされていると文字列などを渡して呼び出した際にエラーが出てくる、という感じ。

 型にはJSのオブジェクトやクラスのようなもので、それをさらに絞り込んだリテラル(実際の値)がある。型にはリテラルも指定できる。

 numberという型には色々なリテラルがあり、1や50などの整数から浮動小数点なども含まれており、これがリテラル、という感じ。

 またconstでの変数宣言だった場合、型は代入されてるリテラルになる(プリミティブ値は少なくともそう)。letやvarでの型指定にはリテラル型も使用できるが、代入ができなくなる。

 型の種類

 any型

 推論に任せてもシステムが分からなかった時のデフォルトの型。最後の手段であり、基本的に乱用は避けるべき。こればかり使ってしまえば、通常のJSとほぼ変わらないため、せっかくの型安全や型チェッカーが死ぬ。

 

 unknown型

 少数ケースだろうけど、型が分からない値の場合anyではなくこれの使用を推奨しているっぽい。

 任意の値を表すものの、チェックすることで型の絞り込みが進むという特性がある。

 比較等でチェックができる(比較自体はできるし、おそらくこの時に型の推論が進む?)ものの、特定の型であることを想定した処理はエラーになる。

 比較、否定、typeofやinstanceof演算子での絞り込みが可能。

 

 boolean型

 真偽値型、ブーリアン。JSと同じくリテラルは2つしかなく、比較等はできるもののあまり多くのことはできない。

 真偽値を変数に代入すると型はbooleanとなる。型にtrueを指定した場合、falseは代入ができなくなる(逆もまた然り)。

 

 number型

 整数、浮動小数点、正数、負数、NaNなどすべての数値の集まり。

 四則演算や剰余、比較などもできる。

 letでnumberだけでなくリテラル型指定ができ、constでり手たる肩を推測させることもできる。

 

 bigint型

 nunberとは独立した型で、丸めのエラーに遭遇せず大きな整数を扱うことができる。

 JSにとってもTSにとっても比較的新しい概念であり、使用する際は注意。すべてのプラットフォームで実装されていないかもしれないので。

 まぁ自分の環境だと使わなさそう。

 

 string型

 名の通り文字列の型。本書には積極的に型推論をさせるべきとある。

 プリミティブ値は基本推論させた方がよさそう。constキーワードでの宣言は代入負荷なだけでなく型がリテラル型にもなるし、イミュータブルな値は積極的にconst宣言すべきなのかもしれない。

 

 symbol型

 ES2015で導入された機能。できることは大きくない、らしい。symbol型は固有の(一意な)値を持つ。例えば文字列aという値のsymbol型を2度生成しても同一にはならない。

 型アノテートの際にuniqueキーワードを併用することで他でいうリテラル型のような振る舞いをする。

 

 オブジェクト型

 オブジェクト型はオブジェクトの中身=形状を指定する。アノテートの段階で中身を記述する感じ。受け取るオブジェクトが明確な場合はこれか。オブジェクトリテラル表記というらしい。

 空のオブジェクトなどでアノテートしても良いが、それだけでは挙動はany型とほとんど変わらないことは注意。

 オブジェクトは特定のプロパティを持つことを重視する構造的型付けという考え方があるようだ。おそらくオブジェクトが特定の型のみを持つことを是とする考え方。

 アノテートの際の形状は通常の記法では必須のプロパティ、識別子の後に?をつけると省略可能を意味する。

 [key : 型]という表記で、特定の型で表されるキーを複数持つ可能性を示唆できる。これをインデックスシグネチャと言うらしい。エディタではこの意味合い以外で出てきたことがあるが、まぁ形状の表記自体を指すこともあるのだろう。はたまた自分の記憶違い。

 フィールド名の前にreadonlyをつけると読み取り専用のフィールドにすることもできる。const宣言のようなものだろう。

 基本的にはオブジェクトリテラル表記(いわゆる形状の表記)かobuject型のアノテートを推奨。何を受け取るか限定できる場合や構造的型付けオブジェクトなら前者、オブジェクトが必要なだけの場合は後者。

 

 型エイリアス

 変数宣言のようにで、typeキーワードで型の別名(エイリアス)を宣言することができる。

 別名の名の通り、本来指している型に置き換えることもできる。型に明確な意図を持たせたい場合や、何度も型を繰り返すのを省く(Don't Repeat Yourselfの略)際に有効。

 

 合併型と交差型

 型アノテートは単一の型以外も指定できる。いずれかの型を持つ場合は合併(union)型、すべてを兼ねる型の場合は交差(intersection)型という。

 or演算子「|」とand演算子「&」でアノテートの際に複数型を列挙できる。

 合併型はいずれか型を持つだけでなく、他方の型も持てる。短絡評価のようにどれかの型がtrueになったら後者の型を持てない、なんてこともなさそうだ。

 交差型は指定した型すべてを持つ。

 

 配列

 配列はやや特殊なオブジェクトであり、型を推論させる場合だと、配列の値が持つ型は初期化された際の型に依存するようだ。

 例えばnumber型を値を入れた配列は、pushで文字列を持たせようとするとエラーとなる。

 空の配列のアノテートだと中身はany型となるが、操作を減ることで型推論が進む。

 

 タプル

  タプルは配列のサブタイプであり、配列を片付けする際の特別な記法だ。インデックスは特定の型を持つ。ただ記法は配列と同じ。

 他と違い、宣言したら明示的アノテートが必要となる。

  性質上、固定長の配列の片付けに近いが、可変長要素もサポートしている。可変長要素となる型の前にドット3つ。デストラクチャリングのようなものだろう。

 

 読み取り専用配列とタプル

 通常の配列はミュータブルだが型アノテートの型の前にreadonlyやReadonlyをつけることでイミュータブル配列やタプルが実装できる。

 性質上明示的アノテートが必要。

 readonlyとReadonlyはやや記法が違う。

 読み取り専用であるが実態はJSの配列と変わらない。破壊的メソッドの使用ができないため、何でもかんでも読み取り専用にすると大変になりそうだ。

 

 null、undefined、void、never

 JSには欠如を表す値として値そのものがないことを示すnullと、未定義を示すundefinedがある。

 Typescriptにはこれに加え、関数が何も返さない場合の戻り値voidと、例外のスローをする関数や無限ループの関数を示すneverがある。

 すべてを包含するunknown型がすべての上位型(スーパータイプ)だとすると、noverは全ての型が持つ下位の型(ボトムタイプ)。どこでも安全に使える。

 …使うかはさておき、どこでも明確に型の意味やできることが指定されてるということだろう。

 

 列挙型

 列挙型はある型について取りうる値を列挙する方法…らしい。

 順序付けされないデータ構造でキーが値となる。配列の逆バージョン、と考えてよいだろうか?

 2種あり、入力された文字列に数字を対応させる数値列挙と、文字列と文字列を対応させマッピングする文字列列挙だ。

 enumキーワードで宣言され丸括弧を省略した関数のような形を持つ。

 例えばa、b、cという文字列を持つ列挙型は、aにアクセスすると0、bで1、cで2と、配列のキーと値を逆にしたような形になる。

 ちょうどアクセス方法もドット記法と各括弧記法だ。

 明示的に値を示すこともでき、キー名 = 値となる。例はこれを省略したデフォルトの数値列挙。

 一度に宣言するのではなく、分割して宣言することもできるらしい。宣言のたびにArray.pushの如くマージしていくとか。

 letやvarキーワードは定義していないキーへのアクセスを許可しちゃうが、const宣言だとこれを許可しない!安全だね!

 

 

 只今4章の関数の項目をやっているので一通りやったら備忘録としてここにアウトプットする。