りあクト!TypeScript で始めるつらくないReact 開発 第3.1 版【Ⅰ. 言語・環境編】を読んで
第1章こんにちはReact
1-1基本環境の構築
Node.js がなぜフロントエンド開発に必要なのか
〇Node.jsってなに?
JavaScript をRuby やPython と同じようにターミナルで動作するもの
※これを使えばJavaScriptをサーバーサイドとして利用できる。
〇フロントエンド開発でNodeが必要な理由
- パフォーマンス最適化のためにJavaScript やCSS フ・ ァイルを少数のファイルにまとめる(バンドル)
- 最新版の機能を使用しているJavaScriptのコードを、ブラウザ実行時に polyfill12 するのではなく
最初からコンパイルしておく - 開発中、ブラウザにローカルファイルを直接読み込ませるのではなく、ローカル環境で開発用の
HTTP サーバを起動してそれ経由でアプリケーションを稼働させる - テストツールを用いてユニットテストや E2E テスト13 を記述する
- ソフトウェアテストやコードの構文解析をローカル環境で実行する
Node.jsのインストール
MAC:Homebrew
Windows:winget
バージョンマネージャ:nodenv
$ brew install anyenv
$ echo 'eval "$(anyenv init -)"' >> ~/.zshrc
$ exec $SHELL -l
$ anyenv install nodenv
$ exec $SHELL -l
〇対話型でのNodeの実行
$ node
> 4 * 8 + 1
> 33
> const foo = 'foo is string';
> foo
'foo is string'
〇ファイル指定でのNodeの実行
node <JavaScript ファイル名>
1-2プロジェクトを作成する
create-react-app
を実行すると以下の最新バージョンがインストールされます。
- react
- react-dom
- react-scripts
■Create React App『Hello, World!』
$ npx create-react-app hello-world --template=typescript
--template=typescript
で作成プロジェクトの下敷きにするテンプレートの指定が可能。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals(console.log);
render メソッドは、第1 引数に渡されたReact のコンポーネントをDOM に描画しなおして第2 引数で指定さ
れたHTML 要素
1-3. アプリを管理するためのコマンドやスクリプト
yarnコマンド
- yarn (install) ………… プロジェクトルートに存在するpackage.json の記述にしたがって、依
存関係のあるパッケージをすべてインストールする - yarn add <PACKAGE_NAME> ………… 指定したパッケージをインストールする
- yarn remove <PACKAGE_NAME> ………… 指定したパッケージをアンインストールする
- yarn upgrade <PACKAGE_NAME> ………… 指定したパッケージを最新バージョンに更新する
(※ package.json で指定された範囲を超えて最新バージョンをインストールするには--latest
オプションを指定する) - yarn info <PACKAGE_NAME> ………… 指定したパッケージについての情報を表示する
- yarn upgrade-interactive [--latest]…………package.json に記述してある各パッケージのバージョン範囲にしたがって、すべてのインストールしてあるパッケージの更新情報をチェック、対話形式で一括アップデートできるコマンド。--latest オプションを指定すると、すべてのパッケージが強制的に最新の安定版バージョンに一括アップデートされる。このときもちろんpackage.json も上書きされる。
react-scripts
'''
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
'''
yarn start を実行したときにreact-scripts start が実行される
※各コマンドの紹介
- start …… 開発用アプ・ リケーションサーバの起動コマンドの登録用
- restart …… 開発用アプリ・ ケーションサーバの再起動コマンドの登録用
- stop …… 開発用アプリケーションサーバの停止コマンドの登録用
- test …… テスト実行開始コマンドの登録用
第2 章エッジでディープなJavaScript の世界
2-1あらためてJavaScript ってどんな言語?
ほかの言語に比べて後方互換性を大事にしてる言語
〇Javascriptが評価されている点
- 第一級関数とクロージャをサポート
- プロパティを随時追加できる柔軟なオブジェクト
- 表現力の高いリテラル記法
2-2変数の宣言
従来の宣言「var」ではなく「const」「let」を使用する。
〇なぜ従来の宣言ではだめなのか?
以下の問題があるため
- 再宣言および再代入が可能
- 変数の参照が巻き上げられる
- スコープ単位が関数
〇何が違うのか
var:再宣言も再代入も可
let:再代入のみ可
const:どちらも不可
2-3. JavaScript のデータ型
Javascriptは「動的型付け言語」だがTypeScriptは「静的型付け言語」である
動的型付け言語
$ node
> let val = 100;
> val = 'foo';
'foo'
静的型付け言語
$ ts-node
> let val: number = 100;
> val = 256;
256
> val = 'foo';
[eval].ts:3:1 - error TS2322: Type '"foo"' is not assignable to type 'number'.
第3章 関数プログラミングでいこう
3-1. 関数型プログラミングは何がうれしい?
□関数プログラミングとは?
参照透過的な関数を組み合わせることで解決するべき問題に対処していく宣言型のプログラミングのスタイル
※『参照透過性』:同じ入力に対して同じ作用と同じ出力が保証されていること
3-2. コレクションの反復処理
□配列の反復処理
関数型プログラミングへの最初の入り口はコレクションの反復処理
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(arr.map((n) => n * 2)); // [ 2, 4, 6, 8, 10, 12, 14, 16, 18 ]
console.log(arr.filter((n) => n % 3 === 0)); // [ 3, 6, 9 ]
console.log(arr.find((n) => n > 4)); // 5
console.log(arr.findIndex((n) => n > 4)); // 4
console.log(arr.every((n) => n !== 0)); // true
console.log(arr.some((n) => n >= 10)); // false
□オブジェクトの反復処理
const user = {
id: 3,
name: 'Bobby Kumanov',
username: 'bobby',
email: 'bobby@maple.town',
};
console.log(Object.keys(user));
// [ 'id', 'name', 'username', 'email' ]
console.log(Object.values(user));
// [ 3, 'Bobby Bear', 'bobby', 'bobby@maple.town' ]
console.log(Object.entries(user));
// [
// [ 'id', 3 ],
// [ 'name', 'Bobby Kumanov' ],
// [ 'username', 'bobby' ],
// [ 'email', 'bobby@maple.town' ]
// ]
Object.keys()'でプロパティのキーリスト、Object.values()でプロパティ値のリストを配列で取得している。 そしてObject.entries()`はキーと値が対になった2次元配列を返す。
3-3. JavaScript で本格関数型プログラミング
□高階関数
引数に関数を取ったり、戻り値として関数を返したりする関数
const greeter = (target) => {
const sayHello = () => {
console.log(`Hi, ${target}!`);
};
return sayHello;
};
const greet = greeter('Step Jun');
greet(); // Hi, Step Jun!
□カリー化と関数の部分適用
カリー化:複数の引数を取る関数を、より少ない引数を取る関数に分割して入れ子にすること
// Pre-curried
{
const multiply = (n, m) => n * m;
console.log(multiply(2, 4)); // 8
}
// Curried
{
const withMultiple = (n) => {
return (m) => n * m;
};
console.log(withMultiple(2)(4)); // 8
}
// Curried with double arrow
{
const withMultiple = (n) => (m) => n * m;
console.log(withMultiple(2)(4)); // 8
}
□閉じ込められたクロージャの秘密
単純なカウンター処理
let count = 0;
const increment = () => {
return count += 1;
};
$ node
> .load open-counter.js
> increment();
1
> increment();
2
> count
2
だが、グローバル変数count に依存してるのがよくない
機能だけを外から使えるように変更し実用性を加味する
const counter = () => {
let count = 0;
const increment = () => {
return count += 1;
};
return increment;
};
$ node
> const counter = (count = 0) => (adds = 1) => count += adds;
> const increment = counter();
> increment();
1
> increment(2);
3
□一般的なメモリのライフサイクル
- 必要なメモリを割り当てる
- 割り当てられたメモリを使用する
- 必要がなくなったら、割り当てたメモリを解放する
3-3. JavaScript で本格関数型プログラミング
JavaScript で非同期処理を扱う基本
const isSucceeded = true;
const promise = new Promise((resolve, reject) => {
if (isSucceeded) {
resolve('Success');
} else {
reject(new Error('Failure'));
}
});
promise.then((value) => {
console.log('1.', value);
return 'Succees again';
})
.then((value) => {
console.log('2.', value);
})
.catch((error) => {
console.error('3.', error);
})
.finally(() => {
console.log('4.', 'Completed');
});
Promise をハンドリングする
最近の非同期処理を扱うライブラリは、ほぼすべてPromise オブジェクトで値を返すようになってる
import fetch from 'node-fetch';
const getUser = (userId) =>
fetch(`https://jsonplaceholder.typicode.com/users/${userId}`).then(
(response) => {
if (!response.ok) {
throw new Error(`${response.status} Error`);
} else {
return response.json();
}
},
);
console.log('-- Start --');
getUser(2)
.then((user) => {
console.log(user);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
console.log('-- Completed --');
});
async / await を使って書き直してみる
import fetch from 'node-fetch';
const getUser = async (userId) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`,
);
if (!response.ok) {
throw new Error(`${response.status} Error`);
}
return response.json();
};
console.log('-- Start --');
const main = async () => {
try {
const user = await getUser(2);
console.log(user);
} catch (error) {
console.error(error);
} finally {
console.log('-- Completed --');
}
};
main();
await を使えば、.then(() => {...}).then(() => {...}).then(() => {...}) なんて面倒なことを書かなくても、
複数の非同期処理を書いたまま順番に実行してくれるわけです
第4 章TypeScript で型をご安全に
4-1. TypeScript はイケイケの人気言語?
現在ではJavaScript コミュニティにおいてTypeScriptを使う人のほうが使わない人よりもだんぜんメジャーになってきている
□なぜ、人気が出てきたのか
静的型付け、型推論、Null 安全性という最近のプログラミング言語のトレンドを押さえつつ、
それ以外の部分はまんまJavaScript と同じ構文を使用できるから
4-2. TypeScript の基本的な型
□型アノテーションと型推論
TypeScript ではvalue: Type というフォーマットで宣言時の変数に型の注釈がつけられる。
※型の不整合があるとコンパイルエラーになる
〇Javascriptとの違い
Javascriptには『暗黙の型変換』があるのでエラーにならない。
$ node
> const s = '123';
> const n = 456;
> s * 3
369
> 246 / s
2
> s + n
'123456'
$ ts-node
> const s = '123';
> const n = 456;
> s * 3
[eval].ts:4:1 - error TS2362: The left-hand side of an arithmetic operation must be of type 'any',
'number', 'bigint' or an enum type.
> 246 / s
[eval].ts:4:1 - error TS2363: The right-hand side of an arithmetic operation must be of type 'any',
'number', 'bigint' or an enum type.
〇VSCodeの便利機能
マウスカーソルをホバーさせると、ポップアップでその型を教えてくれる
□JavaScript と共通の型
インターフェース(Interface):オブジェクトの型に名前をつけることができる
interface Color {
readonly rgb: string;
opacity: number;
name?: string;
}
const turquoise: Color = { rgb: '00afcc', opacity: 1 };
turquoise.name = 'Turquoise Blue';
turquoise.rgb = '03c1ff'; // error TS2540: Cannot assign to 'rgb' because it is a read-only
property.
□Enum 型とリテラル型
TypeScript のenum はデフォルトでは数値で、かつ型安全が保証されない
> enum Pet { Cat, Dog, Rabbit }
> console.log(Pet.Cat, Pet.Dog, Pet.Rabbit);
0 1 2
□タプル型
個々の要素の型と、その順番や要素数に制約を設けられる特殊な配列の型
const charAttrs: [number, string, boolean] = [1, 'patty', true];
□TypeScript でのクラスの扱い
TypeScript のクラスとJavaScript のクラスは、一見似ているようで異なる部分がある。
TypeScript:変数の定義をする必要がある
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;
}
□クラスの2つの顔
TypeScriptにはクラスの型を抽象化して定義する方法が2つある。
- abstract 修飾子を用いて抽象クラスを定義する
- インターフェースを使用してクラスを定義する
interface Shape {
readonly name: string;
getArea: () => number;
}
interface Quadrangle {
sideA: number;
sideB?: number;
sideC?: number;
sideD?: number;
}
class Rectangle implements Shape, Quadrangle {
readonly name = 'rectangle';
sideA: number;
sideB: number;
constructor(sideA: number, sideB: number) {
this.sideA = sideA;
this.sideB = sideB;
}
getArea = (): number => this.sideA * this.sideB;
}
4-4. 型の名前と型合成
□型エイリアスVS インターフェース
型エイリアス:任意の型に別名を与えて再利用できる
type Unit = 'USD' | 'EUR' | 'JPY' | 'GBP';
type TCurrency = {
unit: Unit;
amount: number;
};
interface ICurrency {
unit: Unit;
amount: number;
}
const priceA: TCurrency = { unit: 'JPY', amount: 1000 };
const priceB: ICurrency = { unit: 'USD', amount: 10 };
□共用体型と交差型
共用体型:既存の型を組み合わせて、より複雑な型を表現できる
$ ts-node
> let id: number | string = 208239;
> id
208239
> id = 'a6ba7fb9-8435-4226-804e-387f3d2e53a7';
> id
'a6ba7fb9-8435-4226-804e-387f3d2e53a7'
自分的なメモ
TypeScript Playground:ブラウザで簡単に実行できるサイト