TypeScript チュートリアル

## はじめに

ここでは、TypeScriptをブラウザを使って動作を確認できる環境を作成します。

TypeScript を利用するにあたって、下記を前提としています。  
ミドルウェア等のバージョンが違っていても問題ないことは多いですが、うまくいかない場合はバージョンを疑ってみてください。

* [Node.js](https://nodejs.org/ja/) ver 10.3.0
* [TypeScript](https://www.typescriptlang.org/) ver 2.9
* [Visual Studio Code](https://code.visualstudio.com/) ver 1.24

> 参考
> * [Quick start · TypeScript](https://www.typescriptlang.org/docs/tutorial.html)
> * [第1回　TypeScriptの概要 (1/4)：TypeScriptで学ぶJavaScript入門 - ＠IT](http://www.atmarkit.co.jp/ait/articles/1405/28/news105.html)

## 前提条件

* Node.js がインストールされていること。

> 参考
> * Node.js のバージョン管理マネージャを利用することをお勧めします。
> * windows: [nodist](https://github.com/marcelklehr/nodist)
> * macOS, linux: [n](https://github.com/tj/n)

## npm プロジェクトの作成

1. 適当な場所に空フォルダを作ります。
2. Visual Studio Code(以下 VS Code) を起動し、作ったフォルダを開きます。
3. メニュー「表示」→「統合ターミナル」（ショートカットキー ctrl+@） で、ターミナルを開きます。
4. ターミナルで、下記を実行します。なんかいろいろ聞かれます。名称、バージョン以外は、そのまま enter キー押してしまっても構いません。

> 参考: [初期化処理を行う！npm initの使い方【初心者向け】 | TechAcademyマガジン](https://techacademy.jp/magazine/16151)

   ```
    > npm init
   ```

## TypeScript 関連のインストールと設定ファイルの作成

TypeScript のインストールは、npm を利用します。  
npm のプロジェクトでは、`npm install`を使って、ライブラリをインストールしますが、開発者ごとにバージョン差異が出ないように、ローカルインストールすることが推奨されます。  
参考: [npmのグローバルインストールとローカルインストール - Qiita](https://qiita.com/kijitoraneko/items/175ef29d45d155b3f405)

1. VS Code のターミナルで下記を実行します。

    ```
    > npm install --save-dev typescript
    ```

    `--save-dev` というのは開発時に必要なライブラリであることを示しています。

2. 完了したら、下記コマンドでインストールされたことを確認してください。

    ```
    > ./node_modules/.bin/tsc --version
    ```

    バージョン番号が返ってきたと思います。
3. TypeScriptのコンパイラオプションファイルを作成します。

    ```
    > ./node_modules/.bin/tsc --init
    ```

    フォルダに`tsconfig.json`ファイルが作成できていればOKです。

4. VSCode で`tsconfig.json`を編集します。

    ```json
    {
        "compilerOptions": {
            "target": "es5",
            "module": "commonjs",
            "outDir": "./dist",
            "strict": true,
            "esModuleInterop": true
        },
        "include": [
            "./ts/**/*.ts"
        ]
    }
    ```

    > 参考
    > * [Compiler Options · TypeScript](https://www.typescriptlang.org/docs/handbook/compiler-options.html)

## webpack のインストール

TypeScript は、他のスクリプトファイルを動的に読み込む、モジュール（import / export） の機能を持っています。しかし、TypeScriptのコンパイラはこれを解決しません。

Node.js ではサポートしてるので、このままで良いのですが、ブラウザではこれらをサポートしていないので、対応が必要です。

モジュールを実現する仕組みはいくつかありますが、ここでは現在最もメジャーな`webpack`を利用します。

> 参考:
> * [最新版で学ぶwebpack 4.8入門 - JavaScriptのモジュールバンドラ - ICS MEDIA](https://ics.media/entry/12140)
> * [最新版TypeScript 2.9+Webpack 4の環境構築まとめ(React, Vue.js, Three.jsのサンプル付き) - ICS MEDIA](https://ics.media/entry/16329)
 
1. npm で必要なライブラリをインストールします。

    ```
    > npm install --save-dev webpack webpack-cli ts-loader
    ```

    webpack: 本体。単体だとコマンドラインからの利用はできない

    webpack-cli: コマンドラインで、webpackを実行するためのライブラリ

    ts-loader: webpack で typescript を処理するためのオプション

2. webpack の設定ファイルとなる、`webpack.config.js`を作成します。

    ```js
    const path = require('path');

    module.exports = {
    // モード値を production に設定すると最適化された状態で、
    // development に設定するとソースマップ有効でJSファイルが出力される
    mode: 'development', // "production" | "development" | "none"

    // メインとなるJavaScriptファイル（エントリーポイント）
    entry: './ts/index.ts',

    output: {
        path: path.join(__dirname, "dist"),
        filename: "index.js"
    },

    module: {
        rules: [{
        // 拡張子 .ts の場合
        test: /\.ts$/,
        // TypeScript をコンパイルする
        use: 'ts-loader'
        }]
    },
    // import 文で .ts ファイルを解決するため
    resolve: {
        modules: [
        "node_modules", // node_modules 内も対象とする
        ],
        extensions: [
        '.ts',
        '.js' // node_modulesのライブラリ読み込みに必要
        ]
    }
    };
    ```

## コンパイルのための npm script を書く

コンパイルを簡単に実行するために、npm script を書きます。

package.json

```json
{
    "name": "tstutorial",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "tsc": "tsc",
        "build": "webpack"
    },
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "ts-loader": "^4.4.1",
        "typescript": "^2.9.2",
        "webpack": "^4.12.0",
        "webpack-cli": "^3.0.8"
    }
}
```

## HTML、TypeScriptを書く

1. VSCode でファイル`index.html`を作成します。内容は下記とします。  
    `html:5[TAB]`とタイプすると、HTMLのテンプレートが展開されるので便利です。

    ```html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
        <div id="contents">
        </div>
        <script src="./dist/index.js"></script>
    </body>
    </html>
    ```
2. フォルダ`ts`を作成し、その中にファイル`index.ts`を作成します。  
    せっかくなので、クラスを定義して利用してみます。  
    内容は下記を記述します。

    TypeScriptに対応しているエディタでは、JavaScriptと違い、JavaやC#のような入力支援を受けられることを体験してください。

    ```ts
    class User {
        public age: number;
        public familyName: string;
        public givenName: string;
        constructor (familyName: string, givenName: string, age: number) {
            this.age = age;
            this.familyName = familyName;
            this.givenName = givenName;
        }
    }

    const user = new User('海老原', '賢次', 44); // 名前と年齢は適当に

    const contentsElem = document.getElementById('contents');
    if(!!contentsElem) {
        contentsElem.innerText = `${user.familyName} ${user.givenName}`;
    }
    ```

## tscでコンパイルする

TypeScriptでコンパイルします。

1. VSCode のターミナルから、下記を実行します。

    ```
    > npm run tsc
    ```

2. `./dist/`フォルダに、`index.js`が出力されるはずです。
3. `index.html`を実行して、名前が表示されれば成功です。

> デバッグについて
> ブラウザの開発者ツールで、TypeScriptのソースデバッグするには、ソースマップが必要です。
> TypeScriptのコンパイラオプションで設定できますが、この後のWebPackでもソースマップ付きで出力することができるので、そちらで確認してみましょう。

## モジュールを試してみる

上の例では、クラス定義も1つのファイルの中に書いていました。  
やはりクラス定義は別なファイルに書きたいです。

ここでは、クラスを別ファイルに記述して、`index.ts`から`import`を使って参照してみたいと思います。

1. `ts/User.ts`ファイルを作成し、クラスの記述を移します。  
    `class`の前に、`export`を付けます。これは、他のファイルから直接参照されることを許可するものです。

    > 注) `export`を付けない場合、`export`している他の要素から間接的に参照することは可能です。

    User.ts

    ```ts
    export class User {
        public age: number;
        public familyName: string;
        public givenName: string;
        constructor (familyName: string, givenName: string, age: number) {
            this.age = age;
            this.familyName = familyName;
            this.givenName = givenName;
        }
    }
    ```

2. `index.ts`を修正します。

    import でファイルを参照することができます。

    index.ts

    ```ts
    import { User } from "./User";

    const user = new User('海老原', '賢次', 44);

    const contentsElem = document.getElementById('contents');
    if(!!contentsElem) {
        contentsElem.innerText = `${user.familyName} ${user.givenName}`;
    }

    ```

## webpackでコンパイルしてみる

1. `dist/index.js`を削除してから、下記コマンドを実行してください。  

    ```
    > npm run build
    ```

2. `dist/index.js`が再度作成されるはずです。
3. index.html を再度開いて名前が表示されれば成功です。

## TypeScriptでデバッグ

ブラウザで`F12`キーを押して起動する開発者ツールで、JavaScriptのデバッグができますが、ソースマップを利用すると、TypeScriptのコードのままでブレークポイントや変数の値の確認などができます。

`webpack.config.js` で` mode: 'development'`としておくと、ソースマップが出力されます。

## UUID ライブラリとそのTypeScript定義をインストールする

TypeScriptでJavaScriptのライブラリを利用してみます。

ネタとして、需要が高そうな`UUID`を使ってみます。

1. npm サイトで、ライブラリを検索する

    'npm'(https://www.npmjs.com/)のサイトをブラウザで開きます。

    `search packages`と書いてあるテキストボックスに、`uuid`といれて enter キーを押します。

2. リストの中から良さげなものを選んでクリックします。

    良さげなものとは、ダウンロード数が多く、Popularity 値が高いもの、というのが１つの判断基準になると思います。

    ここでは、そのものズバリ [uuid](https://www.npmjs.com/package/uuid) という名前のライブラリを選択肢ます。

3. 必要条件やインストール方法を確認して、プロジェクトにインストールします。

    VSCode のターミナルで、下記を実行します。

    ```
    > npm install --save uuid
    ```

1. TypeScript定義ファイルをインストールする

    npmで公開しているライブラリは、JavaScriptのコードなので、そのままTypeScriptで利用すると、import や入力支援やビルド時のコンパイルチェックができません。

    そこで、ライブラリごとに提供される TypeScript用の定義ファイルをインストールします。

    TypeScriptの型定義は、やはり npm で `@types/(ライブラリ名)`で提供されています。

    VSCode のターミナルで、下記を実行します。

    ```
    > npm install --save-dev @types/uuid
    ```

    > jQueryやReact、momentなどメジャーなライブラリは、そのほとんどで型定義を公開しています。
    > 中には、ライブラリ本体に型定義を含んでいる場合もあり、その場合は、`@types`をインストールする必要はありません。

## UUID を使ってみる

1. `index.html`を下記のように編集します。

    ```html
    <body>
        <div id="contents">
        </div>
        <!-- 追加 ここから -->
        <div>
            <span id="uuid"></span>
            <button id="saiban" type="button">採番</button>
        </div>
        <!-- 追加 ここまで -->
        <script src="./dist/index.js"></script>
    </body>
    ```
1. `index.ts`を下記のように修正します。

    外部ファイルを参照したときと同じように`import`を利用しますが、`npm install`でインストールしたライブラリを利用する場合は、パスは必要ありません。

    index.ts

    ```ts
    import UUID from 'uuid'; // <- 追加

    import { User } from "./User";

    const user = new User('海老原', '賢次', 44);

    const contentsElem = document.getElementById('contents');
    if(!!contentsElem) {
        contentsElem.innerText = `${user.familyName} ${user.givenName}`;
    }
    // <- 追加 ここから
    // HTMLエレメントを取得 キャストすることで、入力補完が効く
    const saibanButton = document.getElementById('saiban') as HTMLButtonElement;
    const uuidSpan = document.getElementById('uuid') as HTMLSpanElement;
    // ボタンのクリックイベントを追加
    saibanButton.onclick = (e) => {
        uuidSpan.innerText = UUID.v4();
    }
    // <- 追加 ここまで
    ```
1. ビルドして動作確認する

    `webpack`でビルドします。

    ```
    > npm run build
    ```

    `index.html`を開いて”採番”ボタンを押して、UUIDが表示できれば、成功です。

以上