# React勉強会_vol.5 ###### tags:`勉強会中のメモ` ## TS基礎 * 遊び場: https://www.typescriptlang.org/play * 型を省略しても動く→**型推論** * 型アノテーションを省略しても自動的に補完して解釈してくれる ### 配列/オブジェクトの型定義 * 配列は以下が推奨 ```javascript= const numArr: number[] = [1, 2, 3]; ``` * 狭義のオブジェクトの型指定はインターフェースを使う ```javascript= interface Color { readonly rgb: string; opacity: number; name?: string; //プロパティ名の末尾に ? をつけると、そのプロパティは省略可能 } const turquoise: Color = { rgb: '00afcc', opacity: 1 }; ``` * 任意のキーのプロパティ値を定義可能。インデックスシグネチャ ### Enumとリテラル * リテラル * 「~リテラル」とは別。TypeScript用語! ```javascript= let Tom: 'Cat' = 'Cat'; // 'Cat'以外の文字列の代入を許さない Tom = 'Dog'; // エラー! // Enumぽいリテラルの使い方 let Mary: 'Cat' | 'Dog' | 'Rabbit' = 'Cat'; Mary = 'Rabbit'; // これはOK Mary = 'Parrot'; // エラー! ``` * Enumよりリテラルのほうがシンプルなので好まれる ### タプル * 個々の要素の型と、その順番や要素数に制約を設けられる特殊な配列 * 関数の引数とかで使われる概念 ```javascript= // 掲載例 const userAttrs: [number, string, boolean] = [1, 'patty', true]; const [id, username, isAdmin] = userAttrs; console.log(id); // 1 // ちょっといじってみる const [username, id, isAdmin] = userAttrs; console.log(id); // "party" ``` ### Any, Unknown, Never * Unkown: 後ほど出てくる型ガードとあわせて使える * Never: case 文の漏れを未然にチェックできるなど ## 関数とクラスの型 ### 関数 * コンパイラオプションに noImplicitAny が true に指定されてないと、引数の型定義がなくても暗黙の内に any 型があてがわれてコンパイルが通ってしまう * 引数と戻り値を別々に定義するパターン ``` const add = (n: 【引数の型】, m: 【引数の型】): 【戻り値の型】 => n + m; 戻り値がなにも返さないときはvoid const hello = (): void => { console.log('Hello!'); }; ``` * インターフェースで一括で定義するパターン * Reactでコンポーネントを関数で定義するときは引数と戻り値の型をそれ ぞれ定義するのではなく、React.FunctionComponentとして提供されている関数の型を適用することが多い ```javascript= interface NumOp { (n: number, m: number): number; } ``` ### ジェネリクス * ジェネリックプログラミング: データの型に束縛されないよう型を抽象化してコードの再利用性を向上させつつ、静的型付け言語の持つ型安全性を維持するプログラミング手法 * ジェネリクス: 型引数を用いて表現するデータ構造 * 型引数の名前に大文字1文字を使うのは、まあ他の言語でも共通してる慣例 * なんかTSPlaygrondでジェネリクス使えない。。 ### クラス #### 継承 vs. 合成 ```javascript= class Rectangle { readonly name = 'rectangle'; sideA: number; sideB: number; constructor(sideA: number, sideB: number) { this.sideA = sideA; this.sideB = sideB; } getArea = (): number => this.sideA * this.sideB; } ``` * 継承 * 親の変更の影響を受けやすい! ```javascript= class Square extends Rectangle { readonly name = 'square'; // 子クラスではこれだけが公開メンバ変数。 // だが親を継承してるので、sideAとsideBも公開メンバ変数になってしまってる side: number; constructor(side: number) { super(side, side); } // 記述はないが、getAreaメソッドは親のものを共有 // 親の変更の影響を受けやすい! } ``` * 合成 * こちらのほうが保守性高い(とのこと。。) ```javascript= class Square { readonly name = 'square'; side: number; constructor(side: number) { this.side = side; } getArea = (): number => new Rectangle(this.side, this.side).getArea(); } ``` * 最近だと継承は避ける傾向にあるらしい * 継承よりも合成 ### 抽象クラス vs. インターフェース * 抽象クラスは定義に実装を含むことができてしまう * ↑の議論からするに、避けるべきは実装を伴った継承 * インターフェース: 実装を伴わずに型だけを適用できる * Javaのインターフェース(継承先に実装するべきメンバ変数とメソッドを定義するもの)と混同しないこと! * クラスは TypeScript にとって、型でもあり関数でもある ## 型の名前と型合成 * 型エイリアス: 任意の型に別名を与えて再利用できる ### インターフェースとの違い * インターフェース文 * 型の宣言 * 関数でいうと「関数宣言」 * 型エイリアス * すでに作られた型の参照に別名をつけている * 関数でいうと「関数式」 * 今となっては型エイリアスのほうが表現力が高い→インターフェースよりも型エイリアスを使う! ###