りあクト!TypeScriptで始めるつらくないReact開発 第4版【①言語・環境編】の勉強メモ
躓いたところ、わからなかったところ、忘れたところ、まとめたいところをメモします。
りあクト!の解説や他のリソースを参考にまとめていきますので同書籍で勉強されている方の参考になればうれしいです。
※特に表記が無ければ、同書籍の解説・コードを参考にしています。
JSにおけるオブジェクト
狭義のオブジェクト
一般的には「連想配列」と呼ばれるキーと対応する値を持ったプロパティの集まり。多くの場合JSON形式で表現できる。
広義のオブジェクト
プリミティブ型(インスタンスメソッドを持たない型)以外のすべてのもの
=標準組み込みオブジェクトObjectを最終的な継承元に持っているもの
分割代入
右辺と同等の配列やオブジェクトを用意することで、左辺側でそれに対応する値を変数に代入できる
const [a, b] = ['foo', 'bar'];
console.log(a, b); //foo bar
const [, n] = [1, 4];
console.log(n); //4
型推論と動的型付けの違い
-
型推論
コンパイルのタイミングで型が決定され、その型が変更されることはない。
型をプログラマが書くかコンパイラが自動で決めるかという点の違いであり、あくまで静的型付けの世界に閉じた話になる。 -
動的型付け
実行時に型が決まるため、実行タイミングにより型が変化する。
TyepeScriptのインターフェース
インターフェースはクラスが実装すべきフィールドやメソッドを定義した型です。クラスはインターフェースを実装することで、インターフェースが求めるメソッド名や引数の型に則っているかをチェックすることができます。
引用:https://typescriptbook.jp/reference/object-oriented/interface
狭義のオブジェクト的観点からだと「オブジェクトの型に名前をつけるもの」という説明になるが、JavaやPHPなどでいう「インターフェース」(クラスに実装するべきプロパティとメソッドを定義したもの)と本質は変わらない。
interface Color {
readonly rgb: string;
opacity: number;
alias?: string;
}
const tq: Color = {rgb: '00afcc', opacity: 1};
cosnt red: { rgb: string, opacity: number } = { rgb: 'ff0000', oapcity: 1};
と毎回定義してると大変だから、こういう場合にインターフェースを使う。
interface NumOp { (n: number. m: number) : number;
のようにもかけるなど、表現はかなり柔軟。
TypeScriptの呼び出し可能オブジェクト
interface NumOp {
(n: number, m: number) : number,
}
(n: number, m: number)
が引数の型定義、:以降のnumber
が戻り値の型定義
TypeScriptのimplements
クラスがインターフェースを実装するためのキーワード
インターフェースとは、クラスが実装すべきフィールドやメソッドを定義した型のこと。クラスはインターフェースを実装することで、インターフェースが求めるメソッド名や引数の型に則っているかをチェックすることができる。
interface Human {
name: string;
}
class Person implements Human {
name: string; //このメンバーがない場合コンパイルエラー
}
コンストラクタ関数
プロトタイプオブジェクトを継承して、オブジェクトインスタンスを生成するための独立した関数のこと。
Javascriptのクラス構文はコンストラクタ関数のシンタックスシュガーであり、例えばextend
による継承はプロトタイプチェーン(プロトタイプの継承ツリー)を作っている。
クラスを定義する、とはコンストラクタ関数を定義する、ということであり、extendなどで継承されたりnewキーワードで呼び出されたりするとき、コンストラクター関数としての役割を果たすことになる。
Typescriptのインターフェースと型エイリアスの違い
-
インターフェース
- 概念
オブジェクトの型に名前をつけるもの - 形式
- 狭義のオブジェクト(キーと値からなるオブジェクト) で定義
- カーソルをホバーすると名前のみひょうじされる
- セミコロンが不要
- 「Declaration Merging(宣言のマージ)」ができる
後から新しいプロパティの型定義を追加できる
- 概念
-
型エイリアス
- 概念
任意の型に別名をつけて再利用できる機能 - 形式
- リテラル型などの型も定義できる
- インターフェースにも別名をつけられる
- カーソルをホバーすると構造が表示される
- セミコロンが必要 ∵最終的に代入文になるから
- 概念
テンプレートリテラル
バッククォート(``
)で区切られたリテラル。以下できること。
- 複数行の文字列
\n
や文字列連結を使わなくとも改行文字を埋め込める。console.log(`string text line 1 string text line 2`);
- 式の挿入
文字列連結をしなくとも${}
を利用して式を埋め込めるlet a = 5; let b = 10; console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`);
他の利用法は公式ドキュメント参照
参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals
TypeScriptのinferキーワードの動き
type Each<T> = T extends Array<infer U> ? U : T;
const str = 'str';
const arr = [3, 6, 9];
type ArrEach = Each<typeof arr>; //number
type NumEach = Each<typeof str>; //string
型Tが何らかの型の配列だった場合、その配列の中身の型をinfer U
で取得しU
の型としている。Tが配列でなかった場合は、Tの型が出力される。
constにより定義されたオブジェクトの動き
constで定義された変数は、再代入できない。しかしそれはプリミティブ型の場合のみであり、オブジェクト型はプロパティを後から書き換えたり追加できたりする。
例:
type Fig = 'one' | 'two' | 'three' | 'four';
type FigMap = { [key in Fig]?: number };
const figMap: FigMap = {
one: 1,
two: 2,
three: 3,
};
figMap.four = 4;
figMap.one = 0;
console.log(figMap); //{ one: 0, two: 2, three: 3, four: 4 }
このように一度figMapで定義したとしても後からプロパティを追加できたり変更できたりする。
これを防ぐには組み込みユーティリティ型のReadonly<T>
を使う。これを使うことでオブジェクト全体を変更不可にできる。
タプル型
個々の要素の型に加えて、その順番や数に制約を設けられる特殊な配列の型
const charAttrs: [number, string, boolean] = [1, 'patty', true];
使い道
-
関数の引数
→引数でよくつかうレストパラメータも使える。
const spells: [number, ...string[]] = [7, 'heal', 'sizz', 'snooz'];
-
関数の戻り値
関数の戻り値として複数の異なる値を設定したいときはタプル型にしておいて、分割代入で必要な値を抽出してもらうようなインターフェースにすることがある
const userAttrs: [number, string, boolean] = [1, 'patty', true];
const [id, username, isAdmin] = userAttrs;
console.log(id, usename, isAdmin);
文字列とStringオブジェクトの違い(JS)
JSにおいては、Stringオブジェクトとプリミティブ文字列は区別される。
let s_prim = 'foo';
let s_obj = new String(s_prim);
//型
console.log(typeof s_prim); //string
console.log(typeof s_obj); //object
//プロトタイプ
console.log(s_prim instanceof String); //false
console.log(s_obj instanceof String); //true
TypeScriptのオーバーロードの書き方
TypeScriptでは、オーバーロード関数を関数シグネチャと実装の2つの部分に分けて書く必要がある。
//関数シグネチャ部分
function hello(person: string): void; //シグネチャ1
function hello(person: string[]): void; //シグネチャ2
//関数の実装部分
function hello(person: string | string[]): void {
if (typeof person == 'string') {
console.log(`Hello ${person}`);
} else {
console.log(`Hello ${person.join(',')}`);
}
}
-
関数シグネチャ
オーバーロードのパターンを定義する部分。パターン分の関数シグネチャを書く必要がある。
この部分はインターフェースを定義するため、関数のボディは書けない。 -
関数の実装部分
オーバーロードの全パターンを網羅する関数を条件分岐で書く。なお、関数シグネチャと実装部分の関数名は同じにする必要がある。
オーバーロード関数の文法を一般化すると次のようになる。
function 関数名 関数シグネチャ1
function 関数名 関数シグネチャ2
function 関数名 すべてのシグネチャを網羅する実装
参考:https://typescriptbook.jp/reference/functions/overload-functions
Nullish Coalescing と Optional Chaining
Optional Chaining
?.
演算子を用いることで、オブジェクトチェーンの深くに位置するプロパティの値を、チェーン内の各照が正しいかどうかを確認せずに読み込むことを可能にする。
.
や[]
によって指定したキーのプロパティにアクセスできる。指定したキーのプロパティが存していなかった場合、1階層目であればundifined
が返るが、それが2階層目以降であったときにはイプエラーになってしまう。このタイプエラーを避けるため?.
演算子を用いる。
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
const dogName = adventurer.dog?.name;
console.log(dogName); //undifined
console.log(adventurer.someNonExistentMethod?.()); //undefined
参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Nullish Coalescing
Null合体演算子(??
)を用いることで、左辺がnull
またはundefined
の場合にのみ右辺を評価する、という実装をすることができる。
論理OR演算子||
と似ているが、||
は左辺が何らかのfalsyな値('null', 'undefined', false, NaN, 0, -0, 0n, 空文字, document.all)である場合に右辺を評価する。
const foo = null ?? 'default string';
console.log(foo); //"default string"
const baz = 0 ?? 42;
console.log(baz); //0
参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing
yarnでよく使うコマンド
-
yarn [install]
package.json
に登録されているパッケージ、およびそれらと依存関係のあるパッケージをすべてインストールする -
yarn [run] <SCRIPT_NAME>
package.json
に登録されているスクリプトを実行する -
yarn add [-D|--dev] <PACKAGE_NAME>
指定したパッケージをインストール「する -
yarn remove <PACKAGE_NAME>
指定したパッケージをアンインストールする -
yarn upgrade <PACKAGE_NAME>
指定したパッケージを新しいバージョンに更新する -
yarn info <PACKAGE_NAMW>
指定したパッケージについての情報を表示する