TypeScriptの環境構築とコンパイル

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

少しずつ、JavaScriptの勉強をして理解を深めようとしている私ですが、その目的としてDjangoとReact Native(Expo)でWeb/スマホアプリの開発方法を理解しサービスを開発したいというものがあります。
「React Nativeを学べよ！」という声が聞こえたり聞こえなかったりしますが、個人的な感覚で話をすすめると、React Nativeを理解するにはJavaScript/TypeScript/Reactでどういう書き方をするのか、相互の関係性を理解することは避けて通れない気がするのです。

そこで今回はTypeScriptの環境セットアップを行い、ChatGPTの力を借りつつ以前作成したJavaScriptをTypeScriptにあえて変換し、どういう書き方の違いがあるのか等を確認してみたいと思います。

# 参考サイト
TypeScriptを使用するためにはNode.js環境が必要になるみたいです。
私の環境は以下のQiitaで記載している通りNVSを使ってNode.js環境を作っています。

https://qiita.com/ohtsuka-shota/items/0ccde7f9d7e2d9ba3f40

TypeScriptの環境セットアップとコンパイルの仕方は以下を参考にしています。

https://www.sejuku.net/blog/82822

# TypeScriptとは
以前、別のQiita記事でTypeScriptについて以下のように記載したのですが、現場の有識者に雑談レベルで確認してみたところ「変数に型を定義できるだけ」という解釈は間違っていないようでした。

> 変数に型（数値とか文字とか）を定義できるようにしたものらしい。Javaで考えると変数を設定する際に"int"とか"char"とかを指定していたと記憶しているので、JavaScriptでそれを実装するためのものがTypeScriptなんだろうか？と思っておくことにします。
![jser.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/feaa41f9-fd4d-a7c7-20d5-fc5e8b4cb0dc.png)

また、TypeScriptは「人間がコードを書きやすく、読みやすくするためだけにあるもの」というイメージの話を聞かせて頂きました。現にHTMLにJavaScriptを読み込ませる場合、TypeScriptで書いたファイルをいったんコンパイルしてJavaScriptに変換しなければなりません。Javaでいうコンパイル（≒機械語に翻訳する）とも違うので、このTypeScriptのコンパイルの仕方を見ると「確かに人間の為の書き方だな？」と感じました。
※TypeScriptの機能をほとんど利用しないで書いたものであれば、コンパイルしなくてもいいらしいです。

![django-ページ19.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/d67fdd3f-a78e-127b-97cd-e86bc1c06f5d.png)

# 環境構築
Node.jsを準備します。
```
C:\Users\ohtsu\Documents\HTML_dev>nvs use lts
PATH += %LOCALAPPDATA%\nvs\node\20.10.0\x64

C:\Users\ohtsu\Documents\HTML_dev>node --version
v20.10.0
```

TypeScriptをインストールします。
npm(≒Node Package Manager)コマンドでインストール。
tsc(≒TypeSCript) -vでバージョンが出力されれば正常にインストールされていることとなります。
```
C:\Users\ohtsu\Documents\HTML_dev>npm install -g typescript
added 1 package in 4s

C:\Users\ohtsu\Documents\HTML_dev>tsc -v
Version 5.3.3
```

ChatGPTに、以下のQiitaに記載されているSign-upで作成したJavaScriptをTypeScriptでコードしなおしてほしいとお願いしてみました。

https://qiita.com/ohtsuka-shota/items/b6f8f4208aa5c2596193

その結果出力されたコードが以下となります。
```script.ts
document.addEventListener('DOMContentLoaded', function () {
  const signUpForm = document.getElementById('sign-up-form') as HTMLFormElement;
  signUpForm.addEventListener('submit', function (event) {
    const usernameInput = document.getElementById('username') as HTMLInputElement;
    const passwordInput = document.getElementById('password') as HTMLInputElement;

    const username = usernameInput.value.trim();
    const password = passwordInput.value.trim();

    if (username === '' || password === '') {
      alert('すべての必須フィールドに入力してください。');
      event.preventDefault();
    } else if (!/^[a-zA-Z]+$/.test(username) || !/^[a-zA-Z]+$/.test(password)) {
      alert('全ての項目は英字のみを含む必要があります。');
      event.preventDefault();
    }
  });
});

function togglePasswordVisibility() {
  const passwordInput = document.getElementById('password') as HTMLInputElement;

  if (passwordInput.type === 'password') {
    passwordInput.type = 'text';
  } else {
    passwordInput.type = 'password';
  }
}
```
- const signUpForm = document.getElementById('sign-up-form') 
constが変数宣言、signUpFormが変数名になります。TypeScriptにはConstの他に変数宣言が3つあるようです。
    - const：
    一度値を入れたら変更できない。また再宣言（const test = ●● とした後に const test = ▲▲と記述するみたいなこと）もすることが出来ない。
    - var：
    値を変更することも、再宣言することも可能。ブロックスコープを考慮していないため、関数スコープやグローバルスコープとなる。
    - let：
    値を変更することは出来るが、再宣言はすることが出来ない。ブロックスコープを考慮しており、宣言されたブロック内でのみ有効。
    
    ただ、これらの型はJavaScriptでも使用するようです。では違いはなんなのか、ということになりますがTypeScriptだと以下の"number"とか"string"みたいな情報を追加できるようです。このような情報追加が出来ることで可読性の高いコードを作れるみたいでした。
    ```
    TypeScriptの書き方
    let x: number = 10; 
    const y: string = "Hello"; 

    JavaScriptの書き方
    let x = 10; // TypeScriptのnumber型アノテーションは不要
    const y = "Hello"; // TypeScriptのstring型アノテーションは不要
    ```

- as HTMLFormElement;
    as HTMLFormElementとはType Asserionのことで、コンパイラに変数の型を教えることが出来るようです。上記ののは対人間ですが、こちらは対コンピュータということですね。型推論よりもプライオリティが高くなっていいみたいな話が記載されていますが、詳しい話は有識者の解説に委ねます。ここでは「コンパイラに」と記載がありますが型を明記することで「人間に」伝えることにもつながり、その点で上記の"number"や"string"と同様可読性が上がるのかなと見ていて思いました。


https://typescriptbook.jp/reference/values-types-variables/type-assertion-as
    

上記TypeScriptとHTMLや画像などをひとまとめにしたディレクトリを用意しました。TypeScriptはscript.tsというファイル名で保存しています。
```
PS C:\Users\ohtsu\Documents\HTML_dev\form-js03> ls
    ディレクトリ: C:\Users\ohtsu\Documents\HTML_dev\form-js03

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2023/11/26     16:58           4212 contact_user_add_user_new_user_icon_250756.png
-a----        2023/12/29      6:31              0 script.ts
-a----        2023/12/28      7:46            811 style.css
-a----        2023/12/28      7:19           1299 test.html
-a----        2023/12/28      6:30            919 目のアイコン8.jpeg
```

TypeScriptのファイルをコンパイルします。
tscコマンドの引数に.tsを指定し実行すると、コンパイルが実行され.tsファイル名と同じ名前の.jsファイルが作成されていることがわかります。
```
C:\Users\ohtsu\Documents\HTML_dev\form-js03>tsc script.ts

C:\Users\ohtsu\Documents\HTML_dev\form-js03>dir
 C:\Users\ohtsu\Documents\HTML_dev\form-js03 のディレクトリ

2023/12/29  06:40    <DIR>          .
2023/12/29  06:30    <DIR>          ..
2023/11/26  16:58             4,212 contact_user_add_user_new_user_icon_250756.png
2023/12/29  06:40             1,071 script.js
2023/12/29  06:35             1,160 script.ts
2023/12/28  07:46               811 style.css
2023/12/28  07:19             1,299 test.html
2023/12/28  06:30               919 目のアイコン8.jpeg
               6 個のファイル               9,472 バイト
               2 個のディレクトリ  709,257,142,272 バイトの空き領域
```
コンパイルされたJavaScriptのファイルの中身は以下となります。
```script.js
document.addEventListener('DOMContentLoaded', function () {
    var signUpForm = document.getElementById('sign-up-form');
    signUpForm.addEventListener('submit', function (event) {
        var usernameInput = document.getElementById('username');
        var passwordInput = document.getElementById('password');
        var username = usernameInput.value.trim();
        var password = passwordInput.value.trim();
        if (username === '' || password === '') {
            alert('すべての必須フィールドに入力してください。');
            event.preventDefault();
        }
        else if (!/^[a-zA-Z]+$/.test(username) || !/^[a-zA-Z]+$/.test(password)) {
            alert('全ての項目は英字のみを含む必要があります。');
            event.preventDefault();
        }
    });
});
function togglePasswordVisibility() {
    var passwordInput = document.getElementById('password');
    if (passwordInput.type === 'password') {
        passwordInput.type = 'text';
    }
    else {
        passwordInput.type = 'password';
    }
}
```
コンパイルした.jsと以前作成した.jsの差分をWinmergeで確認してみました。
やはりというか、なんというか、同じものが出力されるわけではないのですね～。
感覚ですが、変数定義がコンパイル後のコードのほうがしっかりしている気がします。TypeScriptの思想的に当たり前ですかね？
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/c18179b1-7555-32c2-fcbf-8a76bcab2fbb.png)
処理的に特段変わっている感じも無いので、コンパイルで問題等発生していなさそうです。
![Untitled01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/8530ea13-24e4-2a37-afd3-6060d15b099f.png)