背景

react.dev の公式チュートリアルをやってみました。 hooks にも対応しており良いです。ただしローカル環境構築や TypeScript 関連の案内は無く、相変わらず不親切でした。

リポジトリ: toyboot4e/learn-react

モチベーションの着火

ここ 3 年 web 開発にモチベーションが湧かず、スキル習得を疎かにしてしまいました。そこで外部にモチベーションを求め、 MENTA でメンターを雇ってみました。メンターに毎日進捗報告するために開発をします。虚栄心駆動開発です。

開発中も自分の興味を優先することにしました。 html/css/jsx の記述を高速化できる Emmet に興味を持って、 Emmet を題材としてアプリの作成を考えています 。お金頼り、人頼りでも何でも良いので手を着けて、自身の興味に寄せていけば、最後は自立できると思います。

react.dev を読む

TypeScript + React に入門していきます。

Quick Start

Quick Start では、簡単な例を通して基礎概念の説明がありました:

データを複数の component で共有する場合は、まず親が useState で状態を作り、子へ state を props として渡します。 State を書き換えた場合、変更が伝播して自動的に再度 rendering が走ります。

Tutorial: Tic-Tac-Toe の環境構築

Tutorial: Tic-Tac-Toe ではフレームワークなしで React を使っています。しかしローカル環境の構築方法は示されていません。 create-react-app は deprecated になっていますし、いきなり宙ぶらりんになりました。

プロジェクトの作成 (vite)

今回は vite を使ってみました。 Web 開発におけるビルドツールの 1 つだと思えば良さそうです。

プロジェクトを作成します:

npm create vite@latest 実行
$ npm create vite@latest
✔ Project name: … react-tic
✔ Select a framework: › React
✔ Select a variant: › TypeScript

Scaffolding project in /home/tbm/dev/ts/react-tic...

Done. Now run:

  cd react-tic
  npm install
  npm run dev

指示通りのコマンド実行で dev server (localhost:5173) も起動しました。順調です。

TypeScript の種類

言語選択は TypeScript にしました。 TypeScript + SWC (Speedy Web Compiler) というのもあるようです。

参考. ビルドツールのレイヤ区分

viteesbuild の上に作られているようです。以下の記事が分かりやすかったです:

JavaScriptビルドツールの整理 各ツールの機能と依存関係

ファイル内容の確認

npm create vite@latest によって以下のファイルが生成されました:

プロジェクト構成
.
├── README.md
├── index.html            # ルート
├── package-lock.json
├── package.json          # 依存パッケージ、コマンド定義など
├── public                # 静的アセット (TS からの参照無し)
│   └── vite.svg
├── src                   # ソース
│   ├── App.css
│   ├── App.tsx
│   ├── assets            # 静的アセット (TS からの参照あり)
│   │   └── react.svg
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts     # Vite の環境設定 (?) (公式ドキュメント参照)
├── tsconfig.json         # TypeScript の設定 (言語のバージョンなど)
└── vite.config.ts        # Vite の設定ファイル

以下では特に重要な設定ファイルの内容を確認します。

package.json

Node.js の設定ファイルです。主な初期設定はここに載っています:

package.json
{
  "name": "react-tic",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.43",
    "@types/react-dom": "^18.2.17",
    "@typescript-eslint/eslint-plugin": "^6.14.0",
    "@typescript-eslint/parser": "^6.14.0",
    "@vitejs/plugin-react": "^4.2.1",
    "eslint": "^8.55.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.5",
    "typescript": "^5.2.2",
    "vite": "^5.0.8"
  }
}
tsconfig.json

TypeScript の設定ファイルです。 プロを目指す人のための TypeScript 入門 第九章の通り、追加でオプションを有効化しました:

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
-    "noFallthroughCasesInSwitch": true
+    "noFallthroughCasesInSwitch": true,
+    "noUncheckedIndexedAccess": true,
+    "exactOptionalPropertyTypes": true,
+    "noImplicitReturns": true,
+    "noImplicitOverride": true
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

セットアップ

Tutorial: Tic-Tac-Toe は JS で書かれていますが、 TypeScript を使って写経します。初期状態と対応させるため、 App.tsx を丸ごと書き換えます:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
export default function Square() {
  return <button className="square">X</button>;
}

これで localhost:5173 を開くと、『X』印が表示されるだけの画面となりました。チュートリアルの環境に追いつけました。

追加ツールの導入

volta

volta は JavaScript に対する rustupghcup のようなツールです。

言語サーバ

typescript-language-server を使っています。

prettier

強い (aggressive な) フォーマットを実施するために導入しました。

{
  "scripts": {
+    "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'",
  },
  "devDependencies": {
+    "prettier": "^3.2.4",
  }
}
{
  "singleQuote": true,
  "semi": true,
  "tabWidth": 2,
}

Emmet

Emacs においては emmet-mode を導入すると、 emmet-expand (C-j) によってサクサク html/css/tsx を書けるようになります。

たとえば div<div></div> に展開され、 Square/<Square /> に展開されます。ネストしたデータも記述できる他、構造的編集が可能になるようです。

Typedoc

TypeDoc は (大体) TSDoc 形式のコメントを元に Example にあるようなサイトを生成してくれます。

$ npm i -D typedoc
{
  "scripts": {
    "dev": "vite",
    "doc": "npx typedoc src/main.tsx --skipErrorChecking",
  },
  "devDependencies": {
+    "typedoc": "^0.25.7",
  }
}

TypeDoc の出力には export された関数のみが表示されます:

export { default as Game } from './App.tsx';
export * from './App.tsx';

default export 関数が余分な概念に見えて仕方がありません。 TypeScript Deep Dive にも Avoid Export Default とあるので、禁止してみます。

他の関数へリンクするには、 {@link <name>} という冗長な形式を使用します:

/** Properties of {@link Square}. */
export type SquareProps = {
  value: string;
  isFocused: boolean;
  onSquareClick: () => void;
};

その他は 【TypeScript】そろそろ TSDoc を始めてみる - AI can fly !! などが詳しそうです。

Tutorial: Tic-Tac-Toe の実践

コンポーネントの型表記

公式チュートリアルでは JavaScript が使用されていますが、もちろん TypeScript を使いたいので翻訳が必要です。

おそらくコンポーネントの型はこう書きます:

違いました (FC 使用版)
import { FC } from 'react';

/** Properties of {@link Square}. */
export type SquareProps = {
  value: string;
  isFocused: boolean;
  onSquareClick: () => void;
};

const Square: FC<SquareProps> = ({ value, isFocused, onSquareClick }) => {
  /* ~~ */
}

訂正: Function Components | React TypeScript Cheatsheets によると、 FC は古く、以下の書き方が新しいようです:

/** Properties of {@link Square}. */
export type SquareProps = {
  value: string;
  isFocused: boolean;
  onSquareClick: () => void;
};

const Square = ({
  value,
  isFocused,
  onSquareClick,
}: SquareProps): React.JSX.Element => {
  /* .. */
}

後はゴリゴリやるだけです……!

TypeScript のメモ

まとめ

TypeScript と React に入門し、基本的なツールをセットアップしました。ここまで 4 日です。まだ圧倒的に知識不足ですから、今後多数のプロジェクトを作成して試行回数を増やします。

なお Web 開発は真剣に取り組んだことが無いこともあり、まだ自分が天才である可能性が残っています。などと勘違いを表明するのは独学の弊害ですが、あえてこのまま突っ走ってみたいと思います。