Visual Studio CodeでTypeScript開発のノウハウ (ブラウザ編)

Visual Studio Code & TypeScriptを使い、ブラウザ用の開発設定についてのメモです。以下のような条件の開発設定です。

- 複数のTypeScriptファイルに分割して開発
- TypeScriptのコードは、Node.jsからも利用できる（Module対応）
- Visual Studio Codeからデバッグできる
- async/await構文に対応する

この記事で扱う環境のバージョンは以下の通りです。

- Visual Studio Code : 1.10.1
- Node : 6.5.0
- TypeScript : 2.2.1


#環境構築

基本的な環境構築方法（Visual Studio CodeとNode.js等のセットアップ）については、筆者のページにまとめていますので、[こちら](https://www.sumirelab.com/docs/tech/typescript/visual-studio-code%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%9ftypescript%e9%96%8b%e7%99%ba-%e7%92%b0%e5%a2%83%e6%a7%8b%e7%af%89/)をご連ください。

TypeScript関連のセットアップが終わったら、Visual Studio Codeに「Debugger for Chrome」拡張機能をインストールします。もし、Chromeがインストールされていない場合は、Chromeもインストールします。


#tsconfig.json の記述

TypeScriptのプロジェクトのディレクトリ（.tsファイルが置いてあるディレクトリ）に、「tsconfig.json」というファイルを作成し、設定を記述します。以下のような感じです。

``` tsconfig.json
{
	"compilerOptions": {
	"target": "ES5",
	"module": "amd",
	"sourceMap": true,
	// 出力するファイル名を指定
	"outFile": "./src/main.js",
	"lib": [
            "dom",
            "es5",
            "scripthost",
            "es2015.promise"
	]
	}
}
```

ポイントは3つ

- modlue に amd を設定（AMD形式のモジュールを使用）
- outFile を指定（今回は、./src/main.jsを指定しましたが、ここは場合によって変更してください）
- lib を指定（特に、「es2015.promise」が重要です）

modlueを指定しない場合、トランスパイルされたファイルがoutFileに指定されたファイルにすべて結合されて出力されるため、ブラウザ上で動作させる場合、こちらの方が便利かもしれません。ただ、**Node.jsでこの設定でVisual Studio Codeからデバッガをアタッチした場合、うまくブレークポイントを設定することができませんでした**。そのため、筆者は、ブラウザ上で動作させる場合にもModuleを使用して開発する方針にしています。
modlueにamdを指定しているため、outFileの設定は必須ではありません。ただ、これを設定しないと、tsファイルごとにjsファイルとmapファイルが生成され、ディレクトリが汚れます。そのため、少なくとも開発中は一つのjsファイルにまとめるように設定しています。
libの指定は、async/awaitを使うために、重要です。awaitで待つ関数はPromiseオブジェクトを返さないといけないのですが、ここで指定しておかないと、インテリセンスに文句を言われました。そのほかのライブラリはデフォルトのもののようです。

# .vscode/tasks.json

「.vscode/tasks.json」ファイルがない場合、プロジェクトのビルド（Ctrl + B）を行うと、「タスクランナーが構成されていません。」と出ると思います。そこで、右端の「タスクランナーの構成」をクリックし、「TypeScript - tsconfig.json」を選択します。すると、
「.vscode/tasks.json」が作成されます。内容は特に編集する必要はありません。

![img02.png](https://qiita-image-store.s3.amazonaws.com/0/103854/60288e3d-1589-3cd3-897b-50718a3f5d38.png)

![img03.png](https://qiita-image-store.s3.amazonaws.com/0/103854/27ae5557-6183-d826-8f43-d18713c34a7e.png)


以下のようなファイルが作成されます。

``` .vscode/tasks.json
{
    "version": "0.1.0",
    "command": "tsc",
    "isShellCommand": true,
    "args": ["-p", "."],
    "showOutput": "silent",
    "problemMatcher": "$tsc"
}
```

# .vscode/launch.json

次に、デバッグ用の設定を記述します。デバッグを開始すると、デバッグ構成を選択できますが、デフォルトですとあまりいい感じにならないため、手動で「.vscode/launch.json」ファイルを作成し、以下のように記述します。


``` .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Local",
            "type": "chrome",
            "request": "launch",
            // 読み込むHTMLファイルを指定
            "file": "${workspaceRoot}/index.html",
            "trace" :true,
            "sourceMaps": true,
            "runtimeArgs": [
                "--allow-file-access-from-files"
            ]
        }
    ]
}
```

このように記述すると、デバッグを開始すると、ディレクトリ下の「index.html」ファイルをChromeで開き、デバッグができるようになります。「runtimeArgs」に「--allow-file-access-from-files」を渡しておくことで、ローカルファイルを読み込むようなJavaScriptの実行もデバッグできます。
ちなみに、すでにChromeが起動しているといろいろとうまくいきませんので、一旦終了しておく必要があります（Macの場合は、プロセスを終了させておきます）。もし、起動中のChromeを使用したい場合は、requestにattachを指定し、Chromeの起動設定を変更する必要があります（この記事では詳細は省略します）。

# index.html

さて、Chromeから最初に読み込むHTMLファイルを記述します。以下のような感じです。

```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title> Test </title>
</head>
<body>

    <!-- javascriptの読み込み -->
    <script src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>
    <script data-main="./src/main.js" src="require.js"></script>

</body>
</html>
```

AMD方式でトランスパイルするように設定したため、HTMLからJavaScriptファイルを読み込むためには、RequireJSを使用します。[こちら](http://requirejs.org/)のサイトから「require.js」ファイルをダウンロードし、index.htmlと同じディレクトリに配置してください。そして、

```
<script data-main="./src/main.js" src="require.js"></script>
```

のように`data-main`を使って、対象のJavaScriptファイルを読み込むようにします。また、TypeScriptでasync/awaitを使う場合、Promiseが必要になるのですが、PromiseはIE11では対応していないため、PolyFillのライブラリも読み込むようにしておきます。

# テストしてみる

プロジェクトディレクトリは、以下のようなファイル構成とします。

- .vscode
    - launch.json
    - tasks.json
- index.html
- require.js
- main.ts
- module.ts
- src
    - main.js
    - main.js.map

このうち、「launch.json」「tasks.json」「index.html」「require.js」は先に述べた通りです。また、「src」ディレクトリ以下はビルドすると自動生成されます。main.tsとmod.tsは、以下の通りです。なお、エントリポイントとなるtsファイルの名前とトランスパイル後のjsファイルの名前は同じにしておく必要があるようです。

```main.ts
import Hello = require("./mod");
var hello:Hello = new Hello();
hello.Show();
```

```mod.ts
class Hello
{
    public Show()
    {
        console.log("Hello!!");
    }
}
export = Hello;
```

以下、注意事項のメモです。

- エントリポイントとなるtsファイルとトランスパイル後のjsファイル名とが異なった場合、実行されませんでした…
- module.tsというファイル名にすると、いろいろとトラブりました。おそらく、何かを書き換えてしまったのだと思います。

デバッグを開始すると、Chromeが起動します。ただ、ブレークポイントの設定が間に合わないのか、うまくブレークポイントを設定しても止まらないことがあります。その場合、Visual Studio Codeの方から「再起動」するとうまくいきました。

![img06.png](https://qiita-image-store.s3.amazonaws.com/0/103854/d3db3322-2d67-80cb-c8c0-f4fd8942d0e0.png)

以上の設定で、tsファイル側にブレークポイントを仕掛けてデバッグすることができます。また、TypeScriptのモジュール仕様も利用することができます。（export/import周りの仕様は、別途記事にしていきたいと思います。）

async/awaitも対応しています。例えば、mod.tsを以下のように書き換えます。

```mod.ts

class Hello
{
    showDelay()
    {
        return new Promise( resolve =>
        {
            window.setTimeout(
                ()=>{ 
                    console.log("Hello 2");
                    resolve();
                },
                1000);
        });
    }

    public async Show()
    {
        console.log("Hello 1");
        await this.showDelay();
        console.log("Hello 3");
    }
}
export = Hello;
```

これを実行すると、Hello 1が出力されたのちに、1秒後にHello2→Hello 3と表示されます。IE11でも問題なく動作します。（Promiseについては、[こちら](https://www.sumirelab.com/docs/tech/about_javascript_promise/)で紹介しています。）


# まとめ

筆者のTypeScript環境（ブラウザ開発編）についてまとめました。とりあえず、大規模なものの開発に必要なモジュール部分とデバッガのアタッチ、TypeScriptで使えると便利なasync/await周りを利用できるための設定方法を紹介しました。


###参考

- [RequireJS使い方メモ](http://qiita.com/opengl-8080/items/196213867b859daea719)
- [Visual Studio Codeを使ったTypeScript開発 (環境構築)](https://www.sumirelab.com/docs/tech/typescript/visual-studio-code%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%9ftypescript%e9%96%8b%e7%99%ba-%e7%92%b0%e5%a2%83%e6%a7%8b%e7%af%89/)
- [JavaScriptのPromiseについて調べる](https://www.sumirelab.com/docs/tech/about_javascript_promise/)