概要
https://www.typescriptlang.org/tsconfig#module
module: "ESNEXT"
出力する JS のモジュールの仕組みとして何を使用するかを指定します。
指定できる値は(v4.0.3 現在)
CommonJS
(target がES3
もしくはES5
の場合にデフォルト値)ES6/ES2015
(target がES6
以上の場合にデフォルト値)ES2020
None
UMD
AMD
System
ESNext
になります。
それぞれの値を指定したときにどういったコードが吐き出されるか実際に見たい場合は、公式のドキュメントをご覧ください。
値の方針
- 基本的には未指定で OK。
- targetが適切に設定されていれば、
CommonJS
もしくはES2015
(ES Modules)のどちらか適切な方が使用される。
- targetが適切に設定されていれば、
- v4.0.3 現在、Dynamic Import (動的インポート)を使用する場合、
- 実行環境が ES2020 に対応している場合は
ES2020
を指定する。 - そうでない場合は、
CommonJS
を指定する。 - 詳しくは後述。
- 実行環境が ES2020 に対応している場合は
- 実行環境が ES2020 をサポートしていることを保障できるのであれば、
ES2020
を指定する。- 新しい仕様が出るたびに同じ考え方で指定していく。
- 何らかの事情でそれ以外のモジュールの仕組みを使用したい場合に、該当する値を指定する。
デフォルト値の意味
デフォルト値について要約すると「ES6 よりも前はCommonJS
、ES6 以降はES6
」です。
(ES6/ES2015
っていうのは、 ES Modules のことです)
つまり、ECMAScript の仕様として ES6 からは公式にモジュールの仕組み(ES Modules)が存在するので、「実行環境が ES6 以上なら、非公式の CommonJS じゃなくて公式の ES Modules 使おうね」という意味です。
ES2015
(ES6
)とES2020
の違い
どちらも ES Modules に変わりはないのですが、ES2020 からモジュール関連の新しい仕組みがいくつか策定されました。
module
をES2020
にすれば、それらの機能が Polyfill コードなしで使用できます。
新しい機能というのは Dynamic Import のことです。
(本当は ES2020 からimport.meta
も仕様に入ってるはずなんですが、実際にコンパイルしてみるとimport.meta
は v4.0.3 ではまだESNEXT
に含まれるようです。)
Dynamic Import を使ったコードのコンパイル結果を比べてみましょう。
// ES2020ではトップレベルawaitが使えますが、今回はそれよりも前の仕様にもコンパイルするので統一のため関数で囲いました
(async () => {
const { Hoge } = await import("./hoge"); // Dynamic Import
console.log(Hoge);
})();
上記のような新しい仕様を使った TS コードがあったとして、コンパイル結果は以下。
module 未指定
{
"target": "ES2015"
// "module"は未指定なのでデフォルト値の "ES2015"
}
コンパイル結果
src/index.ts:2:26 - error TS1323: Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'esnext', 'commonjs', 'amd', 'system', or 'umd'.
2 const { Hoge } = await import("./hoge"); // Dynamic Import
~~~~~~~~~~~~~~~~
Found 1 error.
エラーです。
前述の通りmodule
を指定しない場合、デフォルト値は(今回はtargetがES2015
なので)ES2015
になります。
Dynamic Import は ES2020 の ES Modules の新しい仕様であって ES2015 の古い ES Modules の仕様では表現できないので、エラーになります。
module: ES2020
{
"target": "ES2015",
"module": "ES2020"
}
コンパイル結果
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
(() => __awaiter(void 0, void 0, void 0, function* () {
const { Hoge } = yield import("./hoge"); // Dynamic Import
console.log(Hoge);
}))();
正常にコンパイルされました。
注意が必要なのは、targetがES2015
でも、module
がES2020
の場合、モジュール部分のコンパイル結果はES2020
に準拠したコードになるということです。
上記のコンパイル結果を見るとわかりますが、await
は ES2015 に準拠したコードに書き換わっていますが、Dynamic Import 部分はそのままになっています。
module: CommonJS
{
"target": "ES2015",
"module": "CommonJS"
}
コンパイル結果
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
(() => __awaiter(void 0, void 0, void 0, function* () {
const { Hoge } = yield Promise.resolve().then(() => __importStar(require("./hoge"))); // Dynamic Import
console.log(Hoge);
}))();
正常にコンパイルされました。
こちらは見ての通り、Dynamic Import 部分が Pollyfill コードに置き換わっています。
- v4.0.3 現在、Dynamic Import (動的インポート)を使用する場合、
- 実行環境が ES2020 に対応している場合は
ES2020
を指定する。 - そうでない場合は、
CommonJS
を指定する。
- 実行環境が ES2020 に対応している場合は
つまり、v4.0.3 現在、ES2020 に対応していない環境で Dynamic Import を使用する場合はmodule
にCommonJS
を指定する必要があるということになります。