TypeScriptチュートリアル -環境構築編-

**【お知らせ】webpack-dev-serverの起動方法が変わったため少し修正しました(2021/10)。編集リクエストありがとうございました。**

# はじめに
この記事は、JavaScriptからTypeScriptにそろそろ移行したい方向けに書いています。JavaScript自体がちょっとあやふやでも他のオブジェクト指向系の言語の経験があれば理解できるように書いていきます。

# TypeScriptとは
TypeScriptは、JavaScriptの代替となるべくマイクロソフトによって開発されたオープンソースのプログラミング言語です。その特徴は、大規模なアプリケーションの開発に耐えるように、(C++やJavaなどの)クラスベースのオブジェクト指向プログラムの機能や、変数への静的な型の指定などを取り込んで、設計されています。それはJavaScriptの標準仕様であるECMAScript2015以降の新機能を先取りした形にもなっています。


# TypeScriptを動かしてみる

通常、TypeScriptのソースコードは、JavaScriptにコンパイルしてから実行します。そのためTypeScriptコンパイラが必要になります。コンパイラのインストールにはいくつかの手段がありますが、OSを問わずもっとも汎用的に利用できるのはnode.jsに同梱されているnpmコマンドを使ってインストールする方法です。

## TypeScriptコンパイラ導入手順
1. Node.jsをインストール
   - Node.jsの[公式サイト(https://nodejs.org/ja/)](https://nodejs.org/ja/)よりインストーラーをダウンロードして、インストールしてください。ダウンロードできるバージョンは二種類ありますが、ここではバージョン8.9.xの方をインストールします。
2. TypeScriptコンパイラをインストール
    - コマンドプロンプトやターミナルなどを実行し、下記のコマンドを実行してください。
    - ```npm install -g typescript```
    - OSや環境によってはsudoコマンドを先頭につける必要があるかもしれません。 
    - ```sudo npm install -g typescript```
3. インストールの確認
    - 続いてコマンド"tsc -v"と実行し、"Version 2.7.2"などと表示されればインストールは完了です。

## Hello TypeScript
実際にTypeScrptのプログラムを簡単に書いてみて、コンパイルしてみましょう。下記のソースコードをお好きなエディタで書いて、hello.tsという名前で保存してください。

```JavaScript:hello.ts
const message:string = 'Hello! TypeScript!';
console.log(message);
```

そしてコマンドプロンプトもしくはターミナルで、下記のコマンドを実行してください。

```terminal:コマンドプロンプト
tsc hello.ts
```

うまくコンパイルできると、結果として"hello.js"というファイルができているはずです。エディタでコンパイル結果を確認してみてもいいでしょう。
動作確認として、ここでは簡単にNode.jsで実行してみましょう。次のコマンドを入力してください。

```terminal:コマンドプロンプト
node hello.js
```

実行すると、コマンドプロンプトもしくはターミナルで”Hello! TypeScript!”と表示されます。


# 本格的な開発環境の構築
JavaScriptでフロントエンド開発を行うために、様々なツールが登場してきており、それらを使って効率的な開発環境を構築することが、当たり前に行われています。
ここではWebpackというビルドツールを導入し、TypeScriptの自動コンパイル、複数のソースコードの結合、ソースコード更新時の自動リロードを実現してみたいと思います。

## プロジェクトフォルダの作成
まず開発の場としてのフォルダを作ってください。名前は自由ですがここでは"ts_tutorial"としておきます。

```terminal:コマンドプロンプト
mkdir ts_tutorial
cd ts_tutorial
npm init --y
```

ここでは、ついでなので"npm init"コマンドで、package.jsonファイルも生成しておきます。
"--y"というオプションは、package.json内に記述されているバージョン番号や製作者名などをデフォルトの内容でとりあえず作ってしまうものです。もちろん後から自由に書き換えてかまいません。


## Webpackなどツール類のインストール
    
コマンドプロンプトやターミナルなどを実行し、下記のコマンドを実行してください。

```terminal:コマンドプロンプト
npm install typescript ts-loader webpack webpack-cli webpack-dev-server --save-dev
```

ここでインストールしているツール類を表でまとめておきます。

| 名称                      | 説明 |
|:-------------------------|:------------------|
| typescript               | TypeScriptコンパイラ | 
| ts-loader                | TypeScript用のローダー。Webpackと連動してTypeScriptコンパイラを動作させる | 
| webpack                  | JavaScript用のビルドツール（正式にはモジュールバンドラーと呼ぶ） | 
| webpack-cli              | コマンドプロンプトからWebpackを動作させるためのツール。Webpack 4.0から必要になった | 
| webpack-dev-server       | Webpackのビルド、開発用Webサーバー、ソースコードの変更検知、ブラウザの自動リロードを一括して提供するサーバー |

TypeScriptコンパイラは、この少し前にもインストールしているので、重複して入れているように見えますが、ts-loaderはプロジェクト内のライブラリ(node_modulesフォルダ内)にTypeScriptコンパイラが存在していることを前提としていますので、省略せずにインストールしてください。


## 設定ファイルの準備

設定ファイルを準備していきます。
まずはpackage.jsonです。これはnpm用の設定ファイルです。既にほとんどがnpmコマンドによって記入済みのはずなので、"scripts"の部分を修正するだけです。

```JSON:package.json
{
  "name": "ts_tutorial",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --mode=development",
    "start": "webpack serve --mode=development"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "ts-loader": "^4.0.0",
    "typescript": "^2.7.2",
    "webpack": "^4.0.1",
    "webpack-cli": "^2.0.10",
    "webpack-dev-server": "^3.1.0"
  }
}
```

scriptsに追記した内容は、下記の通りです。

- "npm start"コマンドで、webpack-dev-serverを起動するようにする。
- "npm run build"コマンドで、Webpackのビルド処理を起動するようにする。

webpack-dev-serverはビルド処理も行なってくれるため、基本的に"npm start"だけ使っておけば問題ないです。
それぞれのコマンドに付加されているオプション"--mode=development"はWebpack 4.0で追加されたもので、developmentモードで動作させると自動的にソースマップがビルド結果に付加され、"--mode=production"とすると本番モードとして、ビルド結果を圧縮して生成します。


つづいてWebpack用の設定ファイルです。それぞれの意味についてはコメントとして書き込んでいます。

```JavaScript:webpack.config.js
const path = require('path');
module.exports = {
    // モジュールバンドルを行う起点となるファイルの指定
    // 指定できる値としては、ファイル名の文字列や、それを並べた配列やオブジェクト
    // 下記はオブジェクトとして指定した例 
    entry: {
        bundle: './src/app.ts'
    },  
    output: {
        // モジュールバンドルを行った結果を出力する場所やファイル名の指定
        // "__dirname"はこのファイルが存在するディレクトリを表すnode.jsで定義済みの定数
        path: path.join(__dirname,'dst'),
        filename: '[name].js'  // [name]はentryで記述した名前(この例ではbundle）が入る
    },
    // モジュールとして扱いたいファイルの拡張子を指定する
    // 例えば「import Foo from './foo'」という記述に対して"foo.ts"という名前のファイルをモジュールとして探す
    // デフォルトは['.js', '.json']
    resolve: {
        extensions:['.ts','.js']
    },
    devServer: {
        // webpack-dev-serverの公開フォルダ
        static: {
            directory: path.join(__dirname, "dst"),
        },
    },
    // モジュールに適用するルールの設定（ここではローダーの設定を行う事が多い）
    module: {
        rules: [
            {
                // 拡張子が.tsで終わるファイルに対して、TypeScriptコンパイラを適用する
                test:/\.ts$/,loader:'ts-loader'
            }
        ]
    }
}
```

最後はTypeScriptコンパイラ用の設定ファイルです。これはコマンドで生成できます。

```terminal:コマンドプロンプト
tsc --init
```

生成されたファイルは、指定できうるコンパイルオプションを全てコメントで記入されています。
全てここに載せるには冗長なので、有効になっているものだけを抜粋します。

```JSON:tsconfig.json
{
  "compilerOptions": {
      "target": "es5",
      "module": "commonjs",
      "strict": true,
      "esModuleInterop": true   
  }
}
```
最初の節はコンパイルオプションです。それぞれの意味は下記の通りです。

| オプション              | 説明                                      |
|:--------------------|:-----------------------------------------|
| target|コンパイル後のJavaScriptのバージョンの指定 |
| module|コンパイル時にモジュール関連のコードをどの方式として扱うかを指定 |
| strict|暗黙的に利用される"any"型を禁止にするなど、コンパイル時に厳格なチェックを行う           |
| esModuleInterop|CommonJS形式で書かれた外部ライブラリのモジュールを妥当に扱えるようにする。TypeScript 2.7.1から追加された |

## TypeScriptプログラムの作成

簡単なTypeScriptプログラムを作成して動作させてみましょう。
まずは下記のフォルダ階層をプロジェクト内に作成してください。

- ts_tutorial(作成済み)
   - dst
   - src
   - node_modules(npmコマンドによって作成済み) 

"ts_tutorial"フォルダ直下には、先の手順で作成した設定ファイル群が置かれているはずです。
これからつくるTypeScriptプログラムは"src"に作成していきます。
"dst"フォルダ直下にはindex.htmlファイルを作成します。

それぞれのフォルダに下記のプログラムを書いてください。

```JavaScript:src/app.ts
import {Item} from './item';
var elem = document.getElementById('output');
var aBook = new Item('はじめてのTypeScript',1980);
aBook.say(elem);
```
```JavaScript:src/item.ts
export class Item {
    constructor(private name:string, private price:number){}
  
    public say(elem : HTMLElement | null) : void {
        if(elem){ // 引数がnullでない場合
          elem.innerHTML = '書名：' + this.name + '  価格: ' + this.price + '円';
        }
    }
}
```

```HTML:dst/index.html
<!DOCTYPE html>
<html lang='ja'>
  <head>
    <meta charset="utf-8">
    <title>Hello typescript</title>
  </head>
  <body>
    <div id="output"></div>
    <script src="bundle.js"></script>
  </body>
</html>
```

## TypeScriptプログラムの実行

コマンドプロンプトで下記のコマンドを実行してください。これで実行環境であるwebpack-dev-serverが起動します。

```terminal:コマンドプロンプト
npm start
```

つづいてChromeなどなるべく最新のブラウザで、[http://localhost:8080](http://localhost:8080)にアクセスしてください。書籍の情報ぽいものが表示されれば完了です。

<img width="752" alt="s1.png" src="https://qiita-image-store.s3.amazonaws.com/0/24283/7b2499f8-75f3-654a-1d18-16643a656e2f.png">


## 自動リロードを試してみる

今回の設定では、TypeScriptソースコードを変更すると、自動的に再コンパイルおよびブラウザのリロードが行われるようになっています。（webpack-dev-serverの機能です）
これが有効どうかを試してみましょう。
下記のソースコードの値段の部分を例えば1980から2980に変更してみてください。

```JavaScript:src/app.ts
import {Item} from './item';
var elem = document.getElementById('output');
var aBook = new Item('はじめてのTypeScript',2980);
aBook.say(elem);
```

すると自動的に再コンパイルが始まり、ブラウザでの表示が変わるはずです。

<img width="634" alt="s2.png" src="https://qiita-image-store.s3.amazonaws.com/0/24283/701ff23b-1da7-3452-0d83-1420088f68e7.png">