TypeScript
〜 トランスコンパイルと SPA 〜
2026-04-04 作成 福島
TOP > tips > typescript-spa
[ TIPS | TOYS | OTAKU | LINK | MOVIE | CGI | AvTitle | ConfuTerm | HIST | AnSt | Asob | Shell | GBC | LLM ]

0. 前置き

IT 業界では JavaScript を実行するのに JavaScript をコーディングしません。
その代わり、TypeScript を使用して JavaScript のコードを生成します。
JavaScript の型付けを厳密にしたのが TypeScript で、JavaScript のスーパーセット (上位互換) になっています。

型付けが動的なのがスクリプト言語の長所のはずなのに、JavaScript はそのままだと引き起こされる不具合が致命傷になるらしい。
プログラミング言語のソースコードを入力して、別のプログラミング言語のソースコードを出力するプログラムのことをトランスコンパイラと呼びます。
ちなみに、最初期の C++ は、C のソースコードを出力していました。


世界のプログラミング言語の人気順は以下で、TypeScript は上位になっています。
コーディングの作法を教えるより、型チェックで制限をかけるほうが合理的なんだね…。


本稿では、TypeScript を使用して SPA (Single Page Application) を作成します。

TypeScript は業務で使用する*1ので、業務を想定して Linux (WSL2*2) で TypeScript を動作させます。
仕組みを単純化するため React.jsNext.js は使用しません。
Linux は WSL2 の標準ディストリビューションである Ubuntu を使用します。
*1業務は Linux の Docker が前提なので、Windows だけで作成した .ts では差異が生じてしまいます。
*2WSL1 で致命的だった欠陥が、WSL2 ではかなり改善されています。


1. WSL2 のインストール

1-1. WSL2 の実行許可を確認する。
管理者でターミナルを開き WSL2 の実行可否を確認する。
− □ × 
 >_ 管理者: Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # Windows のバージョンが 19041 以上か確認する (Windows10 20H1 以降なら OK)
PS C:\> [Environment]::OSVersion.Version.Build 
26200       ← Windows11 25H2 の場合 (ビルド番号が 19041 以上なので OK)

PS C:\> # wsl コマンドを実行できるか確認する*3
PS C:\> (Get-ComputerInfo)."HyperVisorPresent" 
True        ← True があること

PS C:\> exit 

*3False の場合は BIOS の設定を確認し、Intel VT-x や VT-d, SVM Mode や AMD-V を Enabled にすること。
KVM なら CPU の設定を見直すこと。(XML に cpu mode='host-model' "親 CPU からの引継ぎ" があること)
1-2. WSL2 に Ubuntu をインストールする。
インストール可能な WSL は 1 と 2 が存在する。
Windows のビルド番号が 19041 以上ならデフォルトで WSL2 になるが、ここでは強制する。

インストールする Ubuntu のバージョンは、ポイントリリース*4が 1 以上のものを選択する。
− □ × 
 >_ 管理者: Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # WSL2 をインストールし、バージョンを 2 に固定する
PS C:\> wsl --update 
PS C:\> wsl --set-default-version 2 


PS C:\> # Ubuntu 24.04 をインストールする PS C:\> wsl --install -d Ubuntu-24.04 ダウンロードしています: Ubuntu 24.04 LTS インストールしています: Ubuntu 24.04 LTS ディストリビューションが正常にインストールされました。'wsl.exe -d Ubuntu-24.04' を使用して起動できます Ubuntu-24.04 を起動しています... Provisioning the new WSL instance Ubuntu-24.04 This might take a while... Create a default Unix user account: who ← Ubuntu 用のユーザ名を入力する New password: password ← パスワードを入力する Retype new password: password ← パスワードを入力する (確認) passwd: password updated successfully To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details. PS C:\> # Ubuntu のバージョンを確認する who@pc:/mnt/c/Users/who$ cat /etc/os-release PRETTY_NAME="Ubuntu 24.04.4 LTS" ← 24.04.4 LTS がインストールされた NAME="Ubuntu" VERSION_ID="24.04" VERSION="24.04.4 LTS (Noble Numbat)" VERSION_CODENAME=noble ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=noble LOGO=ubuntu-logo who@pc:/mnt/c/Users/who$ # Ubuntu のホスト名を変更する (必要なら) who@pc:/mnt/c/Users/who$ # ここではホスト名を pc にしている who@pc:/mnt/c/Users/who$ # 設定を変更したあとは必ず wsl --shutdown を実行すること who@pc:/mnt/c/Users/who$ sudo bash -c "cat << 'EOF' >> /etc/wsl.conf [network] hostname = pc generateHosts = false EOF" who@pc:/mnt/c/Users/who$ sudo bash -c "echo '127.0.0.1 pc' >> /etc/hosts" who@pc:/mnt/c/Users/who$ exit logout PS C:\> wsl --shutdown PS C:\> exit
*4Ubuntu のバージョンは YY.MM.R となっており、R (ポイントリリース) が 1 以上で安定する慣例がある。
Ubuntuを入手するを確認して、R が 1 以上になっていない場合はひとつ前 (YY - 2) のバージョンを選択する。
分かりにくいことに、WSL でインストールするバージョンに R を指定することができない。
本稿の記述時点では、まだ 26.04.1 がリリースされていないため 24.04 を指定している。


2. NVM と Node.js のインストール

TypeScript は Node.js を使用して作成されている。
Node.js は更新頻度が高いので複数のバージョンを用意し、切り替えて使用する必要がある。
これを実現させるために NVM (Node Version Manager) をインストールした後に Node.js をインストールする。

NVM は公式の最新版ページの Assets から「Source code (tar.gz)」をダウンロード&インストールする。
今回は v0.40.4.tar.gz だった。

Node.js は NVM を使用してインストールする。

− □ × 
 >_ Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # WSL2 の Ubuntu に入る
PS C:\> wsl ~ 
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.6.87.2-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Sat Mar 28 21:40:40 JST 2026

  System load:  0.4                 Processes:             35
  Usage of /:   0.2% of 1006.85GB   Users logged in:       1
  Memory usage: 11%                 IPv4 address for eth0: 172.29.85.123
  Swap usage:   0%

 * Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
   just raised the bar for easy, resilient and secure K8s cluster deployment.

   https://ubuntu.com/engage/secure-kubernetes-at-the-edge

This message is shown once a day. To disable it please create the
/home/who/.hushlogin file.

who@pc:~$ pwd 
/home/who


who@pc:~$ # NVM の最新版をダウンロードする (最新版は公式ページを参照) who@pc:~$ curl -O -L https://github.com/nvm-sh/nvm/archive/refs/tags/v0.40.4.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 352k 0 352k 0 0 492k 0 --:--:-- --:--:-- --:--:-- 6531k who@pc:~$ # ダウンロードした最新版からインストーラを取り出す who@pc:~$ tar xzf v0.40.4.tar.gz nvm-0.40.4/install.sh who@pc:~$ # NVM をインストールする who@pc:~$ bash ./nvm-0.40.4/install.sh => Downloading nvm from git to '/home/who/.nvm' => Cloning into '/home/who/.nvm'... remote: Enumerating objects: 423, done. remote: Counting objects: 100% (423/423), done. remote: Compressing objects: 100% (350/350), done. remote: Total 423 (delta 59), reused 187 (delta 45), pack-reused 0 (from 0) Receiving objects: 100% (423/423), 413.40 KiB | 1.79 MiB/s, done. Resolving deltas: 100% (59/59), done. * (HEAD detached at FETCH_HEAD) master => Compressing and cleaning up git repository => Appending nvm source string to /home/who/.bashrc => Appending bash_completion source string to /home/who/.bashrc => Close and reopen your terminal to start using nvm or run the following to use it now: export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion who@pc:~$ # .bashrc を現在環境へ反映させる (今回だけ。次回からは不要) who@pc:~$ . ~/.bashrc who@pc:~$ echo $NVR_DIR /home/who/.nvm who@pc:~$ nvm --version 0.40.4
who@pc:~$ # Node.js をインストールする who@pc:~$ nvm install --lts Installing latest LTS version. Downloading and installing node v24.14.1... Downloading https://nodejs.org/dist/v24.14.1/node-v24.14.1-linux-x64.tar.xz... ############################################################################################# 100.0% Computing checksum with sha256sum Checksums matched! Now using node v24.14.1 (npm v11.11.0) Creating default alias: default -> lts/* (-> v24.14.1 *) who@pc:~$ # Node.js のバージョンを確認する who@pc:~$ node -v ; npm -v ; npx -v v24.14.1 ← Node.js のバージョン 11.11.0 ← npm のバージョン 11.11.0 ← npx のバージョン who@pc:~$ exit logout PS C:\> exit


3. プロジェクトディレクトリの作成と TypeScript のインストール

− □ × 
 >_ Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # WSL2 の Ubuntu に入る
PS C:\> wsl ~ 

who@pc:~$ # プロジェクトディレクトリを作成し、Node.js 用に初期化する
who@pc:~$ mkdir -p ./ts/hello/ 
who@pc:~$ cd ./ts/hello/ 
who@pc:~/ts/hello/$ npm init -y 
Wrote to /home/who/ts/hello/package.json:

{
  "name": "hello",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs"
}

who@pc:~/ts/hello/$ # モジュール管理を ES Modules にする
who@pc:~/ts/hello/$ sed -i 's/"type": "commonjs"/"type": "module"/' package.json 

who@pc:~/ts/hello/$ # TypeScript 本体をインストールする*5
who@pc:~/ts/hello/$ npm install typescript --save-dev 
added 1 package, and audited 2 packages in 10s
found 0 vulnerabilities

who@pc:~/ts/hello/$ # TypeScript のバージョンを確認する (tsc = TypeScript Compiler)
who@pc:~/ts/hello/$ npx tsc -v 
Version 6.0.2


who@pc:~/ts/hello/$ # TypeScript の設定ファイルを作成する (./tsconfig.json) who@pc:~/ts/hello/$ npx tsc --init who@pc:~/ts/hello/$ # rootDir, outDir を有効化する who@pc:~/ts/hello/$ sed -i 's@// "rootDir":@"rootDir":@' ./tsconfig.json who@pc:~/ts/hello/$ sed -i 's@// "outDir":@"outDir":@' ./tsconfig.json who@pc:~/ts/hello/$ mkdir -p ./src/ ./dist/
who@pc:~/ts/hello/$ # ソースファイルを作成し、トランスコンパイルする (.ts -> .js) who@pc:~/ts/hello/$ echo 'let message: string = "Hello, TypeScript"; console.log(message);' > ./src/hello.ts who@pc:~/ts/hello/$ npx tsc who@pc:~/ts/hello/$ cat ./dist/hello.js "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let message = "Hello, TypeScript"; console.log(message); //# sourceMappingURL=hello.js.map who@pc:~/ts/hello/$ # 生成された .js ファイルを実行する who@pc:~/ts/hello/$ node ./dist/hello.js Hello, TypeScript who@pc:~/ts/hello/$ exit logout PS C:\> exit
*5インストールする npm パッケージは、JavaScript 実行時の要/不要を選択可能。
TypeScript は変換器 (.ts → .js) であり、.js の実行時は不要なので --save-dev を指定する。
--save   : 実行時に TypeScript を含める。(デフォルト)
--save-dev : 実行時に TypeScript を含めない。


4. じゃんけんサーバの作成と実行

4-1. じゃんけんサーバを TypeScript で作成し、Node.js で実行する。
− □ × 
 >_ Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # WSL2 の Ubuntu に入る
PS C:\> wsl ~ 

who@pc:~$ # プロジェクトディレクトリを作成し、TypeScript 用に初期化する
who@pc:~$ mkdir -p ./ts/janken/ 
who@pc:~$ cd ./ts/janken/ 
who@pc:~/ts/janken/$ npm init -y 
who@pc:~/ts/janken/$ sed -i 's/"type": "commonjs"/"type": "module"/' package.json 
who@pc:~/ts/janken/$ npm install typescript --save-dev 
who@pc:~/ts/janken/$ npx tsc -init 
who@pc:~/ts/janken/$ sed -i 's@// "rootDir":@"rootDir":@' ./tsconfig.json 
who@pc:~/ts/janken/$ sed -i 's@// "outDir":@"outDir":@' ./tsconfig.json 
who@pc:~/ts/janken/$ mkdir -p ./src/ ./dist/ 


who@pc:~/ts/janken/$ # 軽量 Web サーバフレームワークをインストールする who@pc:~/ts/janken/$ npm install express --save who@pc:~/ts/janken/$ npm install @types/express --save-dev
who@pc:~/ts/janken/$ # じゃんけんサーバを作成する who@pc:~/ts/janken/$ cat > ./src/player.ts << EOF import express, { type Request, type Response } from 'express'; const app = express(); app.use('/', express.static('./public')); // <-- これはあとで使う*6 const MY_NAME = process.env.MY_NAME || 'unknown'; const host = process.env.HOST || '0.0.0.0'; const port = Number(process.env.PORT) || 8080; const quiet = process.env.QUIET || false; app.get('/hand', (req: Request, res: Response) => { const remoteAddr = req.socket.remoteAddress const remotePort = req.socket.remotePort // ランダムに手を選択する const hands = ['R', 'P', 'S']; const hand = hands[Math.floor(Math.random() * hands.length)]; if (! quiet) console.log(\`[Hand Requested] From: \${remoteAddr}:\${remotePort} -> Selected: \${hand}\`); // JSONでレスポンスを返す res.json({ name: MY_NAME, hand: hand }); }); app.listen(port, host, () => { if (! quiet) { console.log(\`--------------------------------------------------\`); console.log(\`Janken Server (Express) started on \${host}:\${port}\`); console.log(\`Environment MY_NAME: \${MY_NAME}\`); console.log(\`--------------------------------------------------\`); } }); EOF who@pc:~/ts/janken/$ # トランスコンパイルする who@pc:~/ts/janken/$ npx tsc
who@pc:~/ts/janken/$ # じゃんけんサーバを実行する who@pc:~/ts/janken/$ MY_NAME=jp1 node ./dist/player.js -------------------------------------------------- Janken Server (Express) started on 0.0.0.0:8080 Environment MY_NAME: jp1 -------------------------------------------------- [Hand Requested] From: 127.0.0.1:56734 -> Selected: P ^C ← 停止は をタイプする (下記 4-2 を実行後) who@pc:~/ts/janken/$ exit logout PS C:\> exit
*6index.html 等の静的コンテンツを返す指定。
4-2. じゃんけんサーバをアクセスする。
上記 4-1 と別のターミナルウィンドウで作業する。
− □ × 
 >_ Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # Web アクセスする
PS C:\> curl.exe http://localhost:8080/hand 
{"name":"jp1","hand":"P"}

PS C:\> exit 


5. MC クライアントの作成と実行

− □ × 
 >_ Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # WSL2 の Ubuntu に入る
PS C:\> wsl ~ 

who@pc:~/ts/janken/$ # MC クライアントを作成する
who@pc:~/ts/janken/$ cat > ./src/mc.ts << EOF
import { type Request, type Response } from 'express';

// 指定秒数を待つ関数
const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));

const nodes = [
    'http://localhost:8081/hand',
    'http://localhost:8082/hand',
    'http://localhost:8083/hand',
];

const results: Record<string, number> = { 'jp1': 0, 'jp2': 0, 'jp3': 0 };

// 判定ロジック
function judge(h_a: string, h_b: string, h_c: string): string[] {

    const handsSet = new Set([h_a, h_b, h_c]);

    if (handsSet.size === 3) return []; // グー、チョキ、パー全部
    if (handsSet.size === 1) return []; // あいこ

    const winMap: Record<string, string> = {
        'R': 'S', // グーはチョキに勝つ
        'S': 'P', // チョキはパーに勝つ
        'P': 'R', // パーはグーに勝つ
    };

    // 勝った手を判別する
    const handsArray = Array.from(handsSet);
    const h1 = handsArray[0];
    const h2 = handsArray[1];
    const winnerHand = winMap[h1!] === h2 ? h1 : h2;

    const winners: string[] = [];
    if (h_a === winnerHand) winners.push('jp1');
    if (h_b === winnerHand) winners.push('jp2');
    if (h_c === winnerHand) winners.push('jp3');

    return winners;
}

// メインルーチン(async 関数にする必要がある)
async function main() {

    console.log("Waiting for players to start...");
    await sleep(3000);

    for (let i = 0; i < 10; i++) {
        process.stdout.write(\`Round \${(i + 1).toString().padStart(2)}: \`);

        try {
            // 3人から同時に「手」を取得(並列リクエスト)
            const responses = await Promise.all( nodes.map(
                url => fetch(url).then(r => r.json() as Promise<{ hand: string }>)
            ));

            const h_a = responses[0]!.hand;
            const h_b = responses[1]!.hand;
            const h_c = responses[2]!.hand;

            const winners = judge(h_a, h_b, h_c);
            winners.forEach(w => {
                if (results[w] !== undefined) results[w]++;
            });

            console.log(\`jp1:\${h_a}, jp2:\${h_b}, jp3:\${h_c} -> 勝ち: [\${winners.join(', ')}]\`); 
        } catch (e) {
            console.log(\`Error: \${e}\`);
        }
    }

    console.log('-----');
    console.log('結果:', results);
}

main();
EOF 

who@pc:~/ts/janken/$ # トランスコンパイルする
who@pc:~/ts/janken/$ npx tsc 

who@pc:~/ts/janken/$ # じゃんけんプレイヤーを 3 つ起動する
who@pc:~/ts/janken/$ MY_NAME=jp1 PORT=8081 QUIET=true node ./dist/player.js & 
who@pc:~/ts/janken/$ MY_NAME=jp2 PORT=8082 QUIET=true node ./dist/player.js & 
who@pc:~/ts/janken/$ MY_NAME=jp3 PORT=8083 QUIET=true node ./dist/player.js & 

who@pc:~/ts/janken/$ # MC クライアントを実行する
who@pc:~/ts/janken/$ node ./dist/mc.js 
Waiting for players to start...
Round  1: jp1:S, jp2:P, jp3:S -> 勝ち: [jp1, jp3]
Round  2: jp1:R, jp2:P, jp3:R -> 勝ち: [jp2]
Round  3: jp1:S, jp2:R, jp3:S -> 勝ち: [jp2]
Round  4: jp1:S, jp2:S, jp3:S -> 勝ち: []
Round  5: jp1:P, jp2:R, jp3:R -> 勝ち: [jp1]
Round  6: jp1:P, jp2:P, jp3:S -> 勝ち: [jp3]
Round  7: jp1:R, jp2:R, jp3:R -> 勝ち: []
Round  8: jp1:S, jp2:S, jp3:S -> 勝ち: []
Round  9: jp1:S, jp2:R, jp3:P -> 勝ち: []
Round 10: jp1:P, jp2:R, jp3:P -> 勝ち: [jp1, jp3]
-----
結果: { jp1: 3, jp2: 2, jp3: 3 }

who@pc:~/ts/janken/$ # じゃんけんプレイヤーを 3 つ停止する (ポートを使用しているそれぞれのタスク)
who@pc:~/ts/janken/$ fuser -k 8081/tcp 8082/tcp 8083/tcp 
8081/tcp:             2146
8082/tcp:             2153
8083/tcp:             2160

who@pc:~/ts/janken/$ exit 
logout
PS C:\> exit 


6. 再表示を伴う画面更新

6-1. じゃんけんするページを静的に作成する。
− □ × 
 >_ Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # WSL2 の Ubuntu に入る
PS C:\> wsl ~ 
who@pc:~$ cd ./ts/janken/ 

who@pc:~/ts/janken/$ # じゃんけんプレイヤーに index.html を仕込む (*6を利用する)
who@pc:~/ts/janken/$ mkdir -p ./public/ 
who@pc:~/ts/janken/$ cat > ./public/index.html << EOF
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Janken Player Status</title>
</head>
<body>
    <h1>Janken Player Status</h1>
    <iframe src="/hand" width=300 height=80></iframe>
    <hr>
    <p>ブラウザの 🗘 (更新ボタン) を押してください。</p>
</body>
</html>
EOF 

who@pc:~/ts/janken/$ # じゃんけんプレイヤーをひとつだけ起動する
who@pc:~/ts/janken/$ node ./dist/player.js 
--------------------------------------------------
Janken Server (Express) started on 0.0.0.0:8080
Environment MY_NAME: unknown
--------------------------------------------------
[Hand Requested] From: 127.0.0.1:56996 -> Selected: R
^C      ← 停止は  をタイプする (下記 6-2 を実行後)

who@pc:~/ts/janken/$ exit 
logout
PS C:\> exit 

6-2. じゃんけんページを表示する。
http://localhost:8080/ をブラウザでアクセスすると、上記 6-1 で作成した index.html が表示される。

 <iframe> を使用しているためブラウザは GUI である必要がある。
 ブラウザのリロードボタンをクリックすると、
 じゃんけんプレイヤーがアクセスされることにより、<iframe> の中が更新される。


7. サーバとクライアントを開発

7-1. 定義ファイルの分割とクライアント用 TypeScirpt のトランスコンパイルを実行する
− □ × 
 >_ Windows PowerShell ×   + |
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新機能と改善のために最新の PowerShell をインストールしてください!https://aka.ms/PSWindows  

PS C:\> # WSL2 の Ubuntu に入る
PS C:\> wsl ~ 
who@pc:~$ cd ./ts/janken/ 

who@pc:~/ts/janken/$ # tsconfig.json を再作成する
who@pc:~/ts/janken/$ rm -f tsconfig.json ; npx tsc -init 
who@pc:~/ts/janken/$ sed -i 's@// "rootDir":@"rootDir":@' ./tsconfig.json 
who@pc:~/ts/janken/$ sed -i 's@// "outDir":@"outDir":@' ./tsconfig.json 

who@pc:~/ts/janken/$ # tsconfig.json を分割する (server, client)
who@pc:~/ts/janken/$ cp tsconfig.json tsconfig.server.json 
who@pc:~/ts/janken/$ mv tsconfig.json tsconfig.client.json 

who@pc:~/ts/janken/$ # サーバ用に tsconfig.server.json を変更する
who@pc:~/ts/janken/$ vim tsconfig.server.json 
{
  // Visit https://aka.ms/tsconfig to read more about this file
  "include": ["src/player.ts"],  // 追加
  "compilerOptions": {
    "composite": true,           // 追加

    // File Layout
    "rootDir": "./src",
    "outDir": "./dist",

    // Environment Settings
    // See also https://aka.ms/tsconfig/module
    "module": "nodenext",
    "target": "esnext",
    "types": ["node"],           // 変更
    ……

    // Recommended Options
    "strict": true,
    "jsx": "react-jsx",
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "noUncheckedSideEffectImports": true,
    "moduleDetection": "force",  // モジュール化 (確認*7)
    "skipLibCheck": true,
  }
}
who@pc:~/ts/janken/$ # クライアント用に tsconfig.client.json を変更する who@pc:~/ts/janken/$ vim tsconfig.client.json
{
  // Visit https://aka.ms/tsconfig to read more about this file
  "include": ["src/client.ts"],  // 追加
  "compilerOptions": {
    "composite": true,           // 追加
    "lib": ["DOM", "ESNext"],    // 追加

    // File Layout
    "rootDir": "./src",
    "outDir": "./public/js",     // 変更

    // Environment Settings
    // See also https://aka.ms/tsconfig/module
    "module": "nodenext",
    "target": "esnext",
    "types": [],
    ……

    // Recommended Options
    "strict": true,
    "jsx": "react-jsx",
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "noUncheckedSideEffectImports": true,
    "moduleDetection": "force",  // モジュール化 (確認*7)
    "skipLibCheck": true,
  }
}
who@pc:~/ts/janken/$ # ビルド用に tsconfig.json を再作成する who@pc:~/ts/janken/$ cat > tsconfig.json << EOF { "files": [], "references": [ { "path": "./tsconfig.server.json" }, { "path": "./tsconfig.client.json" } ] } EOF
who@pc:~/ts/janken/$ # クライアント用の /js/ ディレクトリを作成する who@pc:~/ts/janken/$ mkdir -p ./public/js/ who@pc:~/ts/janken/$ # クライアント用の .ts を作成する who@pc:~/ts/janken/$ cat > ./src/client.ts << EOF /** * サーバから「手」を取得して画面を更新する関数 */ async function updateHand(): Promise<void> { const resultDiv = document.getElementById('result'); try { // サーバの /hand をアクセスする const response = await fetch('/hand'); if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } // JSON データの受信 const data: { name: string; hand: string } = await response.json(); // DOM の書き換え if (resultDiv) { // サーバから返ってきた "R", "P", "S" をそのまま表示 resultDiv.innerText = \`出した手: \${data.hand}\`; } } catch (error) { console.error('通信に失敗しました:', error); if (resultDiv) { resultDiv.innerText = 'エラーが発生しました。'; } } } /** * イベントリスナーの登録 * ページロード時に HTML のボタンと関数を紐付ける */ window.addEventListener('DOMContentLoaded', () => { const btn_pon = document.getElementById('btn_pon'); if (btn_pon) { btn_pon.addEventListener('click', updateHand); } }); EOF
who@pc:~/ts/janken/$ # index.html を再作成する who@pc:~/ts/janken/$ cat > ./public/index.html << EOF <!DOCTYPE html> <html> <body> <h1>Janken Client</h1> <span id="result">じゃん、けん…</span> <button id="btn_pon">ポン</button> <script src="/js/client.js" type="module"></script> <!--*7--> </body> </html> EOF
who@pc:~/ts/janken/$ # じゃんけんシステムをビルドする who@pc:~/ts/janken/$ npx tsc -b who@pc:~/ts/janken/$ ls -ogh ./public/index.html ./public/js/client.js ./dist/player.js -rw-r--r-- 1 1.2K Mar 31 14:38 ./dist/player.js -rw-r--r-- 1 205 Mar 31 14:37 ./public/index.html -rw-r--r-- 1 1.2K Mar 31 14:38 ./public/js/client.js
who@pc:~/ts/janken/$ # じゃんけんプレイヤーをひとつだけ起動する who@pc:~/ts/janken/$ node ./dist/player.js -------------------------------------------------- Janken Server (Express) started on 0.0.0.0:8080 Environment MY_NAME: unknown -------------------------------------------------- [Hand Requested] From: 127.0.0.1:35866 -> Selected: R ^C ← 停止は をタイプする (下記 7-2 を実行後) who@pc:~/ts/janken/$ exit logout PS C:\> exit
*7 tsconfig にモジュール指定を記述すると HTML に type="module" が必要となる。
7-2. じゃんけんページを表示する。
http://localhost:8080/ をブラウザでアクセスすると、上記 7-1 で作成した index.html (と /js/client.js) が表示される。

ボタンをクリックすると、
 じゃんけんプレイヤーがアクセスされることにより、手が変化する。(R,P,S のどれか)