TypeScript初心者がステップ・バイ・ステップで開発環境の構築手順を説明する

初めてTypeScript＆Angularに手を出すことになり、開発環境を構築しましたので初心者目線から構築手順をシェアします。

僕のMacには、HomebrewもVisual Studio Codeもすでにインストールされているので以下の手順はその前提です。また、全てのコマンドはVSCode内のターミナルから実行しています。なお、本記事で対象としているバージョンは以下の通りです。

+ macOS: High Sierra 10.13.6
+ Node.js: 10.12.0
+ TypeScript: 3.1.3
+ Angular CLI: 6.2.5
+ Angular: 6.1.10
+ Karma: 3.0.0
+ Jasmine: 2.99.0
+ VSCode: 1.28.1

## Node.jsのインストール
まずNode.jsをインストールします。Node.jsが入っているかどうか分からない場合は、以下のコマンドでバージョンを確認します。バージョン番号が返されればインストールされています。

```bash
$ node -v
-bash: node: command not found
```

[@kyosuke5_20](https://qiita.com/kyosuke5_20) さんの [MacにNode.jsをインストール](https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09) を参考に、以下の3点に注意してしてインストールします。

+ ``nodebrew install latest``コマンドが失敗する
nodebrewで最新のnodeをインストールしようとした際に ”Failed to create the file” というエラーが発生した場合は、 ``mkdir -p ~/.nodebrew/src``のようにディレクトリを作成しましょう。

+ インストールしたnodeにパスを通す
インストールしたnodeにパスを通す際に、permission deniedで書き込みできないことがあります。その場合は以下のようにsudoを使いましょう。
``$ sudo　echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile``

+ インストールしたnodeの有効化
インストール直後には使用するバージョンが有効化されていません。忘れずに``nodebrew use %バージョン%``で使用するバージョンを指定しましょう。

## TyepScriptのインストール 
Node.jsのインストールが終わるとnpmが使えるようになります。以下のコマンドで TypeScript をインストールしましょう。

```bash
$ npm install -g typescript
```

## Angular-CLIのインストール
続いてAngularのコマンドラインツールをインストールして、基本的な構成の初期プロジェクトを新たに作りましょう。新しいプロジェクトを作成すると一緒にAngularのライブラリ群もインストールされます。便利です。

```bash
$ npm install -g @angular/cli
```

以下のコマンドを使って基本的な構成でプロジェクトを作成します。プロジェクト名に"_"（アンダースコア）は使用できません。エラーになります。プロジェクト名の単語区切りはハイフンを使いましょう。

```bash
$ cd /path/to/projects
$ ng new %プロジェクト名%
```

プロジェクトフォルダができあがり、各種ファイルが初期状態で作られます。できあがったサンプルは以下のコマンドで起動しブラウザ上に表示させることができます。

```bash
$ ng serve --open
```

この画面が表示されたということは、環境面はバッチグーということになります。
![angular-startup.png](https://qiita-image-store.s3.amazonaws.com/0/245269/352fdb3b-37c9-63ff-4cde-8cdeadbdf1d9.png)
ちなみにアプリを終了するには ``Control+C`` です。

# Visual Studio Codeおよびプロジェクトの設定
ここからは、TypeScriptアプリケーションの開発環境であるVSCodeで、機能拡張のインストールとプロジェクト環境セットアップを行ないます。

## インストールした機能拡張
VSCodeには以下の機能拡張をインストールしてあります。実際にまだコードを書き始めていないのでこれから追加でインストールするかもしれませんが今の所は最小限の追加にとどめています。

+ Debugger for Chrome
Chromeでデバッグする際の必須ツールのようです。まだデバッグしたことがないのでよく分かっていません。

+ TSLint
TypeScript用の静的コード解析ツールです。TypeScript素人なのでコーディング規約から外れていないかツールに教えてもらわないとよく分かりません。

## tsconfig.jsonの設定
``tsconfig.json``ファイルには、TypeScriptをJavaScriptにコンパイルするための設定情報が含まれ、プロジェクトのルートに置かれます。oaTypeScript初心者の僕には内容を説明できないので [@tomof](https://twitter.com/tomof) さんの [js STUDIO](http://js.studio-kingdom.com/typescript/project_configuration/tsconfig_json) を参照してください。ちなみに、 ``tsc --init`` を使用した場合と ``Angular CLI`` の ``ng new`` を使用した場合で、できあがるtsconfig.jsonは以下のように異なっているようです。

``tsc --init``で作成されたtsconfig.json（コメントアウト除外）

```json
{
  "compilerOptions": {
    "target": "es5", 
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true
  }
}
```

Angular CLIの ``ng new`` で作成されたtsconfig.json

```json
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "es2015",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "dom"
    ]
  }
}
```

この差がどう影響するのか今の僕にはよく分からないので、とりあえずAngular CLIが出力したデフォルトのまま使用してみます。また、この設定だとコンパイル済みのテストコード（〜.spec.ts）とソースコード（〜.ts）のコンパイル結果が、同じ出力ディレクトリに吐き出されそうで不安な気持ちになります。

>2018年10月23日更新
>[@k3nNy_51rcy](https://qiita.com/k3nNy_51rcy) さんからコメント欄で解決策を教えてもらいました。ありがとうございました！

>コンパイル時に`tsc`ではなく`ng build`を使えば、spec.tsファイルはビルド対象から外されて出力ディレクトリには出力されません。`spec.ts`ファイルのビルドを除外する方法で著名なのは、`tsconfig.json`に以下のような除外設定を書く方法ですが、これだと`ng test`実行時にダンマリになってしまいます。

>```json:tsconfig.json
>"exclude": [
    "**/*.spec.ts",
  ],
>```

>`ng build`はテストコードの除外設定がある`tsconfig.app.json`を使い、`ng test`は除外しない設定の`tsconfig.spec.json`を使うという仕組み（angular.jsonの設定）になっています。ですからAngular CLIで作成したプロジェクトではルートの`tsconfig.json`でテストコードの除外を設定する必要がなく、単純に`ng build`を実行するだけで万事解決です。

テストとソースのフォルダを分けるJava、プロジェクトそのものを分けるC#のテストに慣れた身からすると何となく嫌な感じがするのですが、それもTypeScriptプロジェクトの流儀だということなら慣れたいと思います。何事も食わず嫌いは良くないですしね。😅

## task.jsonでビルドの設定
以下のようにコマンドパレットに"task"と入力するとリストが表示されるので、``規定のビルドタスクを構成する``を選択します。
![task.png](https://qiita-image-store.s3.amazonaws.com/0/245269/37f5d8aa-fe7c-2f59-a7f4-18d29d8c160d.png)
すると以下のリストが続けて表示されるので、プロジェクトルートのtsconfig.jsonを指す ``tsc:ビルド`` を選択します。
![build.png](https://qiita-image-store.s3.amazonaws.com/0/245269/68a7b5c3-1775-3471-9687-aac8fa0d7a3e.png)
すると ``.vscode/task.json`` が以下のようなデフォルト状態で作成されます。

```json
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "typescript",
            "tsconfig": "angular-sample/tsconfig.json",
            "problemMatcher": [
                "$tsc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}
```

ここでターミナルから ``tsc`` と入力するか、 ``Command+Shift+B`` を入力するか、"ターミナル"メニューから”ビルドタスクの実行"を選択すると、tsファイルがビルドされ、 ``tsconfig.json`` の ``outDir`` に設定されているディレクトリ（上記例では ``dist/out-tsc``）にjsファイルが出力されます。

## launch.jsonでデバッグ起動の設定
続いて、デバッグ起動の設定を行います。アクティビティバーでデバッグアイコンをクリックするとサイドバーに``構成がありません``と表示されています。
![not-configured.png](https://qiita-image-store.s3.amazonaws.com/0/245269/8b1bdc3f-b3b0-d0e6-c393-ca4deb3a3720.png)
該当コンボボックスをクリックすると、以下のようなリストが表示されるので該当のプロジェクト（この例では ``angular-sample``）を選択します。
![select-config.png](https://qiita-image-store.s3.amazonaws.com/0/245269/d29de895-be2b-5120-820a-0a24e872ea30.png)
さらに、コマンドパレットで、該当プロジェクトのデバッグで使用する実行環境を ``Chrome`` と選択します。
![select-chrome.png](https://qiita-image-store.s3.amazonaws.com/0/245269/c043ad44-1b77-2c74-7d77-c90cdc5810cf.png)
すると、``.vscode/launch.json`` が以下のようなデフォルト状態で作成されますので、url欄のポート番号は8080ではなく4200に変えてください。これは後述するAngular Live Development Serverのデフォルトのリッスンポートが4200だからです。

```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Launch Chrome against localhost",
            // 8080を4200に変えること！
            "url": "http://localhost:8080", 
            "webRoot": "${workspaceFolder}"
        }
    ]
}
```

ここまで設定が完了したら、まずVSCodeのターミナルに ``ng serve`` と入力して "Angular Live Development Server" を起動します。"Complete Successfully" とういうメッセージがターミナル上に表示されたら、F5を入力するかデバッグ開始ボタンをクリックしてデバッグを起動してみましょう。

![debug-chrome.png](https://qiita-image-store.s3.amazonaws.com/0/245269/e5254fe3-4e82-570d-aae6-76566dfe10b5.png)

無事、Chromeで上記のようなページが表示されました。デバッグ時にいちいち ``ng serve`` を実行するのは面倒ですが、一度起動してしまえばずっとそのままで良いのであまり気にならないかもしれません。

## ユニットテストの設定
JavaでもC#でも、長年ユニットテストを書いてきましたしカバレッジも80%以上をキープしてきました。TypeScriptでも同じようにユニットテストを実施して品質を保証したいと思います。ありがたいことに、Angular CLIで作られたプロジェクトには、ユニットテストがすでに組み込まれていますので特に何も設定する必要がなく、ただ単に以下のコマンドでテストを走らせるだけです。

```bash
$ ng test
```

Chromeが自動的に起動してユニットテストを実行します。Chrome画面には以下のようにテスト結果が表示されますが、かなり見にくいのでなんとかしたいところです。

![unit-test.png](https://qiita-image-store.s3.amazonaws.com/0/245269/cd88c32f-f17c-1603-0ffe-f4ee4ed6592c.png)

さらに、カバレッジを計測してくれるオプションがあります。ユニットテストの実行コマンドに以下のようなパラメーターを付加すると、カバレッジを計測して結果を ``coverage`` ディレクトリにHTML形式で書き出します。

```bash
$ ng test --code-coverage
```

出力されるHTMLは以下のようなものになります。この例では、src/appの分岐条件網羅率がよろしくないという結果になっています。

一覧
![coverage-list.png](https://qiita-image-store.s3.amazonaws.com/0/245269/745f77f9-7d6b-79c8-193e-3e6619970753.png)
具体的なコードを見てみると、app.component.tsに"22"の条件分岐があり"10"しかテストされていないことになっています。このソースのどこに条件分岐が22もあるのかと問いたい。小一時間問い詰めたい。（.jsの方を見ろというのはわかっています）

コード
![coerage-code.png](https://qiita-image-store.s3.amazonaws.com/0/245269/0a9f63d3-7cef-300c-b098-089b6bb215d5.png)
賛否両論あると思いますが、はっきり言って見にくいです。C#でOpenCoverとかDotCover、JavaでCloverとかEclEmmaを使っていた身にはキツイです。HTMLのページで、カバーされていないと判定された箇所を確認し、該当のソースを開いて修正するというUXも好きになれません。

そこで、VSCodeの機能拡張から [Coverage Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters) というプラグインをインストールします。これで以下のようにカバーされているか否か直接ソースコード上で確認できるようになり、かなり好みのUXになってきました。
![coverage-gutters.png](https://qiita-image-store.s3.amazonaws.com/0/245269/63ed09b8-8e4e-b987-3ce5-ddc950512903.png)
さらに``karma.conf.js``の以下のセクションに手を加えると閾値を設定することができます。以下の例では全部80%以上であることを強制する仕様になります。

```js
coverageIstanbulReporter: {
  reports: [ 'html', 'lcovonly' ],
  fixWebpackSourcePaths: true,
  thresholds: {
    statements: 80,
    lines: 80,
    branches: 80,
    functions: 80
  }
}
```
もし、一つでも80%を切っている項目があれば、``Coverage for branches (45.45%) does not meet global threshold (80%)``のようなメッセージがターミナルに表示されます。

また、コードカバレッジを毎回``ng test --code-coverage``で実行するのも面倒なので、``angular.json``の``test``セクションに以下の記述を追加すると、テスト実行時に自動的にカバレッジも計測されるようになります。

```json
"test": {
  "options": {
    "codeCoverage": true
  }
}
```

最後にエンド・ツー・エンドです。ユニットテストではなく画面操作から一連の流れを、Seleniumを使用してテストする場合は以下のコマンドを実行します。

```bash
$ ng e2e
```

テストの初期化を行い、Chromeが起動してWelcome画面を表示した後、自動的にChromeが終了します。うちでは、エンド・ツー・エンドのテストはVSTSのUIテストを使うんだけど、ひょっとしたら中身は一緒かも？（よく分かってないので後で調べます）

# 終わりに
これで環境構築はひとまず終了です。テストコードがソースと同じ場所にあることや、インデントがスペース2つという文化、さらにコードカバレッジのコレジャナイ感に馴染まなければいけないと思うと気が重いですが、おいちゃんがんばります。