TypeScriptを活用してのWebアプリケーションの作成（コンパイルとバンドル）

この動画はプログラミングチュートリアルさんの「本当の初心者からTypescript×Webpackの開発環境構築までをハンズオン形式で学ぼう！」をまとめたものです。

https://www.youtube.com/watch?v=ECc1EXnx7VQ

そもそもTypescriptとは、

Javascript + 型付 = Typescript

このように型付という機能を付与しただけなのがTypescript(以下、TS)である。
型付という機能を実装することで静的型付言語となる。

そして、そもそも型とは何か。

JS(Javascript)での変数宣言は以下のようになる。
```
//変数の再宣言が出来る定義
var list = []:
var suuzi = 1;

//変数の再宣言が出来ない定義
const name = "プログラミング";
```

型の宣言がないため、変数に何を入れるのか入れるまでわからない。

一方でTSでの変数宣言は以下のようになる。
```
//変数の再宣言が出来る定義
var list:String[] = [];
let suuzi:Number = 1;

//変数の再宣言が出来ない定義
const name:String = "プログラミング";
```

TSの場合、変数名定義の後、型の宣言を行う。
そうすると、例えば"suuzi:Number"という変数にString型の"１"を入力するとエラーが返ってくる。
このように宣言した型以外の値を代入するとエラーが返ってくる。

まとめると、JSに比べてより厳格なルールなのがTSである。

実際に動作させるときはTSをコンパイルしてJSに書き換える。

TSのメリットとしては、
・コンパイル時にエラーチェックが出来る。これによってより厳格なアプリケーションの作成が可能。
・型がついているので可読性があがる。
・型のおかげ余分なメモリを確保しなくて済むため(?)プログラムの処理速度が速い。


ここからは実践に移る。

VSCodeでプロジェクトを作成するフォルダを作成する。
そして、パッケージ管理のため以下のコードをターミナル上に打つ。
```
npm init -y
```

そして、nodeも使うためインストールしているかを確認する。
```
node -v
```

そして、今回のプロジェクトの開発環境でのみ使えるようなかたちでTSをインストールする。
```
npm i --save-dev typescript
```
-Dと--save-devは同じ役割を果たす。現在の開発環境でのみ使えるようにTSをインストールすることが出来る。

インストールが完了したら"package.json"の"devDpendencies"に"typescript"が加わる。そして、".bin"というディレクトリに"tsc.cmd"というディレクトリが作成させる。つまり、tyscというコマンドが使えるようになるということ。このコマンドを用いて、コンパイルしていく。

```
npx tsc -v
```

こうすることでtscが環境パスなどを指定することなく使えることが分かる。

typescriptの記述をしていく。プロジェクトのディレクトリ中に"tutorial.ts"というファイルを作成する。
JSであれば変数の定義を以下のようにする。
```
const a = 3;
const b = a;
```
しかし、TSでは型の定義が必要である。
以下のように型を定義してやるとエラーが起こる。
これはString型の変数として定義した"b"に対してNumber型で定義した"a"を代入しているからである。

```
const a: Number = 3;
const b: String = a;

console.log(b);
```

この状態でとりあえずコンパイルしてみる。
コンパイルの仕方は次のとおりである。ターミナル上でコードを打つ。
```
npx tsc tutorial.ts
```

そうすると、エラーが表示される。

```
tutorial.ts:2:7 - error TS2740: Type 'Number' is missing the following properties from type 'String': charAt, charCodeAt, concat, indexOf, and 43 more.

2 const b: String = a;
        ~


Found 1 error in tutorial.ts:2
```

これが、TSのメリットとしてあげたコンパイル時に型のエラーを事前に検知してくれるというもの。

そして、コンパイルを行うと"tutorial.js"というファイルが作成される。ちなみに、この"tutorial.js"を実行してもエラーは発生しない。

そして、"tutorial.ts"ファイルのbの型をNumberに変更すると、コンパイルしてもエラーをはかない。
ただし、"tutorial.js"が作成され"b"が定義されているためこれについてはエラーが起こる。

![Untitled (34).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2971002/72ceea8a-c137-fcd3-caf4-0e0ce13a6213.png)

```
let array1 :Array<number> = [1,2,3,4];
let array2 :number[] = [0,1,2,3,4];
let array3 :number[] = [];
array3 = array2;
console.log(array3);
```

このように、Number型を要素に持つ配列にNumber型の配列を代入しても、コンパイル時にエラーを出すことはない。ただし、例えばarray3の定義を"string[]"でした場合、これはエラーが発生する。

そしてここから、ウェブパックの利用をしていく。
これはモジュールハンドラーと言って複数のファイルをまとめる、そして管理することに役立つ。

これも開発環境でインストールしていく。
```
npm i -D webpack webpack-cli webpack-dev-server ts-loader
```

これで、"dev-server"はローカルサーバーを立ち上げる事ができる。そして、"ts-loader"はTSをコンパイルするときに使う。

そして、ウェブパックをカスタマイズしていく。

ここに、どの複数のファイルをまとめるか。そして、まとめたフォルダの名前をどうするのか。そして、出力先をどうするのかなど詳細なカスタマイズ設定を1つのファイルに書いていく。

"webpack.config.js"というファイルを作成して以下のようにコードをうっていく。

```
module.exports = {
    entry: {
        bundle: "./src/index.ts", 
    },
    output: {
        path: `${__dirname}/dist`,
        filename: "[name].js",

    },
    mode: "development",
    resolve: {
        extentions: [".ts", ".js"],
    },
    devServer: {
        static: {
            directory: `${__dirname}/dist`,
        },
        open: true,
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                loader: "ts-loader",
            }
        ],
    }
};
```

最初の"module.exports"の"entry/bundle"という部分は複数ファイルをまとめる際に始点となるファイルを設定している。

次の"output"の部分ではバンドルしたファイルの保存先と保存形式を示している。

次の"mode"では開発時に使用する"development"と製品版をより軽い形式で出力する"production"がある。

そして、"resolve"という部分では拡張子の記述がない場合、自動補完してくれるように記述している。

続いて、devServerという部分であるがこれはよくわからない。

最後の"module"は拡張子が".ts"のファイルを"ts-loader"を用いてJSファイルに変換するというコンパイルする命令を記述している。

これらのカスタマイズはGitHubなどに上げて雛形として使い回す。

そして、続いてTS用の設定を行う必要もある。下のコードをターミナル上に入力する。
```
npx tsc --init
```

そうすると、"tsconfig.json"が作成される。そして、これに関しては必要なカスタマイズのコメントアウトを外してカスタマイズしていく。

そして、コメントアウトされているtarget属性はこれはコンパイルした後のJSのバージョンを指定している。

```
"target": "es2016",
```

これだと、アロー関数など最新の関数などが使えるが、逆にこのバージョンを古いものにするとアロー関数などの記述は自動的に古いものに置き換えられる。

詳細な設定方法を調べたかったら「tsconfig　設定」などで調べるとより詳しく知れることが出来る。

そして、TSを活用するとクラスを活用出来るのでクラスを使ってWeb上に文字を表示してみる。

プロジェクトディレクトリ内に"saySomething.ts"ファイルを作成して記述していく。
```
export default class SaySomething {
    message: string;

    //初期化
    constructor(message: string) {
        this.message = message
    }

    public sayText(elem: HTMLElement | null) {
        if(elem){
            elem.innerText = this.message;
        }
    }
}
```

このようにすることでString型のメッセージを使ってインスタンス化出来るクラスを作成した。HTML上のelementを取得して、messageを写すというのがこのクラスの役割。

そして、ElementIDを取得するためにindex.htmlにdivタブを作成する。

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>
```

```
import SaySomething from "./saySomething";

const root: HTMLElement | null = document.getElementById("root");

//インスタンス化
const saySomething = new SaySomething("Hello TypeScript");
saySomething.sayText(root);
```

そして、index.tsではHTMLファイルのdivタブを取得し、さらにクラスをインスタンス化して使用する記述をした。

そして、package.jsonファイルを書き換えてページを表示できるようにする。
scriptのtestの部分を削除して下のように書き換える。

```
"scripts": {
    "start": "webpack serve"
  },
```

そうすると、ターミナル上で以下のように打つとサーバーが起動するようになる。
```
npm start
```

ちなみに私はこのときエラーを起こしてしまった。

```
C:\Users\yusuk\typescript-project\typescript-tutorial>npm start

> typescript-tutorial@1.0.0 start
> webpack serve

[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
 - configuration.resolve has an unknown property 'extentions'. These properties are valid:
   object { alias?, aliasFields?, byDependency?, cache?, cachePredicate?, cacheWithContext?, conditionNames?, descriptionFiles?, enforceExtension?, exportsFields?, extensionAlias?, extensions?, fallback?, fileSystem?, fullySpecified?, importsFields?, mainFields?, mainFiles?, modules?, plugins?, preferAbsolute?, preferRelative?, resolver?, restrictions?, roots?, symlinks?, unsafeCache?, useSyncFileSystemCalls? }
   -> Options object for resolving requests.
```

エラーの原因は"extentions"のスペルを間違えたためうまく読み込まれなかったことである。
これを修正してもう一度ウェブを起動するとうまくいった。

そして、何も表示されないのはコンパイルとバインドが出来ていないためである。
そのため、webpackのカスタマイズに従ってコンパイルをしていく。

"package.json"ファイルをまた書き換えてこれらを実行していく。Scriptに書き換えていく。

```
"scripts": {
    "start": "webpack serve",
    "build": "webpack"
  },
```

そして、ターミナル上でこれを起動していく。

```
npm run build
```

そうすると、disディレクトリに"index.ts"と"saySomething.ts"のファイルをコンパイルしてバンドルしたものを作成する。

そして、index.htmlの中にbundle.jsを反映させる記述をする。
コード内の下部にある<script src="bundle.js"></script>という記述。

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
    <script src="bundle.js"></script>
</body>
</html>
```

そして、もう一度ウェブを起動する。すると、このようにきちんと表示される。

![Untitled (35).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2971002/13eafa8d-fd95-a00b-a2cb-1a957ebb38c2.png)

そして、"webpack.config.js"のmodeを"devlopment"から"production"にすると、作成されるbundle.jsが圧縮されて軽いかたちになるようにコンパイルとバンドルをしてくれる。

```
(()=>{"use strict";var e={607:function(e,t,r){var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const o=s(r(236)),n=document.getElementById("root");new o.default("Hello TypeScript").sayText(n)},236:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(e){this.message=e}sayText(e){e&&(e.innerText=this.message)}}}},t={};!function r(s){var o=t[s];if(void 0!==o)return o.exports;var n=t[s]={exports:{}};return e[s].call(n.exports,n,n.exports,r),n.exports}(607)})();
```