Open16
読書メモ りあクト!①言語・環境編

第3章 関数型プログラミングでいこう
参照透過性
- 同じ入力に対して同じ作用と同じ出力が保証されていること
パラダイム
- プログラミング言語の多くは命令形プログラミング・宣言型プログラミングの2つに大別される
命令形
- 手続き型プログラミングが代表的なパラダイム
- グローバル変数としてまとめたデータを、複数の手続きを経て最終結果を得るプログラミングスタイル
- Goも手続き型言語!
- オブジェクト指向は命令形・宣言型という分類からは独立したパラダイム。
- 現代なメジャーな言語(Java・Ruby・C++・・・etc)は、オブジェクト指向と手続き型両方の性質を兼ね備えたもの
宣言型
- 出力を得る方法ではなく、出力の性質・あるべき状態を宣言する事でプログラムを構成
- SQLが代表的な例
- これらの式を組み合わせて処理を作成するスタイルが関数型プログラミング
関数型の特徴
- 式を組み合わせて処理を作っていく
- 文で値を得るためには副作用を用いる必要があるた、それにくらべ式は値を返すため副作用を少なくプログラミングしやすい
- 以下のコメント?がわかり易かった
- https://github.com/rubima/rubima/issues/66#issuecomment-43441526

コレクションの反復処理
- 代表的な関数型のメソッドは大体知っていた
- sliceを活用してシャローコピーする or スプレッド構文で配列を展開する事で破壊的メソッドの副作用を回避するテクニックは知らなんだ
- for of VS forEach
- どちらかというとforEachが良いが、中で外部の変数を変更するような使い方になっているケースが多いため、極力どちらも使わない方が良い

オブジェクトの反復処理
- このあたりは知っている内容が多かった

改めて関数型プログラミングとは何か
- Rubyのブロック構文は関数型第一級関数をサポートしてないRubyで無名関数と同等の機能を実現するための苦肉の策
- 高階関数
- 関数に引数として関数を渡したり、その戻り値に関数を設定出来る
- 引数として渡される関数の事をコールバック関数という

非同期処理
そもそもJSはユーザとのインタラクションが多いため、それらを止めないためにも非同期処理を活用しなければならない
Promise
非同期処理を扱うためのオブジェクト
メリット
- callbackだとネストして可読性が低いが、thenでつなげて書くことが出来て可読性が高い
const promise1 = new Promise((resolve, reject) => {
console.log('開始')
resolve('成功');
reject('失敗'); // resolveが実行された後は実行されない
});
promise1.then(
(value) => {
console.log(value);
return "次のチェイン";
},
(error) => {
console.log(error);
}
).then(
(msg) => {console.log(msg)}
);
> "開始"
> "成功"
> "次のチェイン"
async await
- asyncを付与する事で返される値が暗黙の内に
Promise.resolve()
によってラップされた相当のものになる - 非同期関数の中では、他の非同期関数をawait演算子をつけて呼び出す事が出来る。
- await式によって非同期関数を実行すると、そのPromiseが
resolve()
もしくはreject()
されるまで文字通り待ってもらえる
↑のPromiseの例をasync を使って書き直すとこんな感じ?たしかにthenでつなぐよりは、見易い。
async function promise1() {
console.log('開始')
return "成功";
}
async function promise2(value) {
console.log(value);
return "次のチェイン";
}
async function promise3(value) {
console.log(value);
}
async function main() {
const msg1 = await promise1()
const msg2 = await promise2(msg1)
promise3(msg2)
}
main()

Javascriptの例外処理
基本的なtry~chatchに関するものはあるが、例外種別に応じて処理を分けたりとかは出来ない

TSの章
null安全という概念について
null安全は恐らく知らない概念とおもったけど、Java書いてる時にIntelliJで指摘されてた気もするな
オブジェクトの型
- オブジェクトの型を定義するのにプロパティのキー名と値の型を明記している
- これらをinterfaceとして用意出来る
- readonlyは書き換え不可
- ?は省略可能なプロパティ
インデクスシグネチャ
- キーとデータの型だけを指定する方法
- そのままでは使えないらしい
enum
- そのままだと値がデフォルト数値になっていて、型安全が保証されない
- 2.4.0から導入された文字列enumを使えば解決する
リテラル型
- 任意の文字列意外を許さない型
- これを
|
で複数繋いで並べる事によってあたかも列挙型のように扱える、これが人気らしい
タプル型
- 配列の順番・型・数に制約をかける型
- 関数の引数に使ったりする
any 、unknown、never
- any
- いかなる型の値でも受け付けるようになる
-
JSON.parse
の戻り値がanyなので、存在し無いプロパティにアクセスする事も可能になってしまう- これによるnullpointexeptionが起きてしまう。せっかく型があるにも関わらず。
- unknown
- TS ver3.0から追加された、anyの型安全ver
- 任意の型を代入出来る点は同じであるが、型ガードという値の型を特定する処理を追加して値にアクセスする必要がある(後述)
- never
- 何も代入出来ない型
- switch文のdefaultの代入先に指定したりして、case文の記載漏れを防ぐみたいな用途で使われるらしい

関数とクラスの型
呼び出し可能オブジェクト
- 関数の型を呼び出し可能オブジェクトとして、関数定義とは別でinterfaceとして定義する事が出来る
- インターフェースの定義にも使える
ジェネリクス
Javaと同じ概念なので問題なし
TSの可変長引数
扱えるという事だけ現時点では理解した

TypeScriptでのクラスの扱い
-
JSと異なり、予めプロパティを暮らすの最初で宣言しておく必要がある
- 初期値を設定してコンストラクタでの初期化を省略する事が可能である
- readonlyを付与して、プロパティを変更不可にも出来る
- javaのようなアクセス修飾子を付与する事も可能
-
継承より委譲はTSやReactの世界でも同様な模様
-
Javaと同じInterfaceが存在する
-
TSでクラスを宣言すると、クラスインスタンスのインターフェース型宣言と、コンストラクタ関数宣言の両方が行われる
- クラスはインスタンス化することも可能だし、インターフェースのように型として扱う事も可能
- ちなみにinterfaceはクラスを継承出来る

型エイリアス
型エイリアス VS インターフェース
- インターフェースの他にtypeで型が定義出来る
- typeは既に存在する型に別名を与えて参照する機能
- インターフェースは同じ名前で複数個インターフェースを定義出来る(これってどうなのかと思うが)
- 一般的にはインターフェースが良く使われているらしい
ユニオン型
- 型を組み合わせて利用出来る
- 型A or 型Bのように利用出来る
- null許容にしたい時はユニオン型で明示する
インターセクション型
- オブジェクト型の合成に使われる
- 非nullアサーションは型チェックの恩恵を破壊するものなので原則使用すべからず
型表現に使われる演算子
- typeof演算子で、既存の変数から型を抜き出す事が出来る
- in演算子でリテラル型からキーの値を限定する事が出来る
- keyof typeofと組み合わせる事で既存オブジェクトからキーの型を抽出出来る
型を関数のように使うという概念がなかったので新鮮だった - as cost
- まとめてリテラルの定数にするというイメージ
- valueOfも無理やり実装する事が出来る

配列から型を生成する部分は難しかった...

条件付き型とテンプレートリテラル
条件付き型
- 参考演算子とextendsキーワードを使って任意の条件による型の割当が出来る
- こんなのがあるのか...機能豊富
- 型を取得するために型の関数を使うのか
-
T['id']
みたいに書けばキーの型を取得出来るのか
-
テンプレートリテラル
- 型にテンプレートリテラルを適用出来る
- 文字列ベタ書きしてたところを型で縛る事が出来る
組み込みユーティリティ型
章末にも同じ事がかかれていたが、こういうのがあると頭の片隅にいれた

関数のオーバーロード
- TSでもJavaのようにオーバーロードが可能
- ただし、一つの関数に中身を全て集約する必要がある
asによる型アサーション
-
変数 as 型名
で特定の型とみなしてその変数を扱う事が可能- キャストのように型を変換しているわけではないので、型が異なるとエラーになる
- これは最後の手段らしい
4.6.2 型ガードでスマートに型安全を保証する
- 型ガードは処理前に型をチェックしましょうねという話だと理解した
- ユーザ定義の型ガードは、記述語という表現方法を活用した関数をユーザが独自に用意し、それで型チェックを行うと理解した
- クラスを下敷きにしてないただのオブジェクトではinstance of関数が使えないため
型の表現豊か過ぎて、だいぶ開発者のレベルを問う言語だなという感想。
linterとか静的解析ツールとか組み合わせて使わないと、大変な事になりそう。

4-7-3 モジュールの型はどのように解決されるか
- TSでは変数宣言空間と型宣言空間が分かれている
- 同じファイル内において同じ名前で宣言された変数と型は、1つのimport文でまとめてインポート・エクスポート出来る(これをコンビネーションというらしい)
- ただ、TS3.8からは型のみインポート・エクスポートが出来るようになった
- 型をサポートしているライブラリとそうでないものがあり、中には手動で型を取得する必要のあるライブラリが存在する
- ほとんどtypesyncを利用すれば問題なし
- トリプルスラッシュディレクティブ
///
でコンパイラに直接参照すべき型定義を伝える事が出来る- これでスタイルファイルやメディアファイルを読み込んだ時どう表示するか指定可能
こういうの一度理解しておけばトラブル時に考えようがあるから助かる

4-8 TypeScriptの環境設定
4-8-1 コンパイラオプションstrict
- コンパイルする時に付けるオプションはtsconfig.jsonの設定値によって変わる
- 複数の型の制約などを1オプションでまとめて有効にしてくれる
- ただ、verupで勝手に制約が追加されてコンパイルが通らなくなる事がある
他はむずい、困った時に見直そう