tsconfigのmoduleとtargetには何を設定すればいいか - TypeScript

TypeScriptの設定ファイルを雰囲気でコピらないための記事

## TL;DR
- `module`はどのモジュールパターンで出力するかを指定する
    - Node.jsでの利用想定なら`commonjs`
    - ブラウザ側での利用なら`umd`
- `target`はどの動作環境向けにトランスパイルするかを指定する
    - 利用環境が想定できないなら`es3`や`es5`
    - 下位互換性のあるコードへの変換をBabelで行うなら`esnext`

## module とは

JavaScriptにはいくつかのモジュールパターン（CommonJSやAMD、ECMAScriptなど）がある。TypeScriptをJavaScriptに変換する際、どのモジュールパターンにするかを`module`に指定する必要がある。

`module`には`'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'esnext'`などを指定できる。デフォルトは`target === "es3" or "es5" ? "commonjs" : "es6"`、つまりtargetがes3かes5ならcommonjsとなる。

### サンプルコード
今回は実際にアウトプットされるコードを見ながら解説する。
用いるTypeScriptのサンプルコードは以下。

```index.ts
export default (name: string) => {
  console.log(`Hello ${name}`)
}
```

### `module: commonjs`

commonjsはサーバー側（Node.js）で用いられるモジュールパターン。
このモジュールは`require('name')`形式で読み込める。

自分のコードがNode.jsからしか利用されない、という想定の場合はcommonjsにする。

`yarn run tsc index.ts --module commonjs`

```output.js
"use strict";
exports.__esModule = true;
exports["default"] = (function (name) {
    console.log("Hello " + name);
});
```

余談だが、後述するアウトプットの中にも`exports.__esModule = true;`のような記述が頻出する。これはモジュールがES Modules（`import`, `export`）形式に対応していることを伝えるためである。読み込み側はTypeScriptの`esModuleInterop`をtrueにすれば、上のようなcommonjs形式のライブラリも`import`で読み込める。

詳しくは「[TypeScriptの`--esModuleInterop`は一体何をやっているのか - stone's throw](http://osamtimizer.hatenablog.com/entry/2018/06/28/122502)」が参考になる。

### `module: amd`

amdはブラウザ側で利用でき、さらに非同期で読み込めるモジュールパターン。
amdについては[AMD &amp; RequireJS - Qiita](https://qiita.com/nanocloudx/items/70f1316debf05b93ac82)が参考になる。

`yarn run tsc index.ts --module amd`

```output.js
define(["require", "exports"], function (require, exports) {
    "use strict";
    exports.__esModule = true;
    exports["default"] = (function (name) {
        console.log("Hello " + name);
    });
});
```

### `module: umd`

umdは、commonjsとamdの両方に対応したモジュールパターン。コードサイズはamdとそれほど変わらないため、基本的にブラウザ向けに配布する場合もumdにする。（例として、[Vue.jsはscriptをumdで配布している](https://jp.vuejs.org/v2/guide/installation.html)。）

`yarn run tsc index.ts --module umd`

```output.js
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports"], factory);
    }
})(function (require, exports) {
    "use strict";
    exports.__esModule = true;
    exports["default"] = (function (name) {
        console.log("Hello " + name);
    });
});
```

### `module: es6`
`yarn run tsc index.ts --module es6`

ライブラリがES Modules形式でしか利用されない場合、`es6`もしくは`esnext`を指定する。
しかし前述したcommonjsのライブラリも`esModuleInterop`によってES Modules形式で読み込むことができる。そのため無理にこの`es6`などに設定してcommonjsを切り捨てる必要は無い。

```output.js
export default (function (name) {
    console.log("Hello " + name);
});
```

## Target

`target`はどの動作環境に向けてトランスパイルするかを指定する。

`target`には、`'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext'`などを指定できる。（デフォルトは`es3`）

### `target: es3`

サンプルコードにある``Hello ${name}``は、ES6から使える[テンプレートリテラル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/template_strings)である。ES3やES5では使えないため、ここでは`"Hello " + name`に変換されている。

`yarn run tsc index.ts --target es3`

```output.js
"use strict";
exports.__esModule = true;
exports.default = (function (name) {
    console.log("Hello " + name);
});
```

### `target: es6`

`es6`ではサンプルコードと同じ結果になる。

しかしES6はまだブラウザでは動作しないため、前述した`es3`や`es5`で出力しなければいけない。

このような下位互換性のあるコードに変換する役割はTypeScriptでも担えるが、Babelを用いて行うケースも多い。そういった場合はTypeScriptは`es6`または`esnext`として出力し、Babel側で任意の環境に向けたコードにトランスパイルするのが一般的。

`yarn run tsc index.ts --target es6`

```output.js
export default (name) => {
    console.log(`Hello ${name}`);
};
```