この記事は、2022年におけるTypeScript製ES Modulesの作成方法を解説、共有するためのものです。
node.jsにおけるCommonJSの歴史は長く、それに関する記事が大量にあります。どの設定がCommonJSに関わり、どの設定がES Modulesに関わるのかを読み解くには労力がかかります。この記事ではPure ESM作成の最小限構成を通して、ES Modulesの設定を整理します。
node.jsのメジャーバージョンが更新された場合、この記事のレシピは適用できない可能性があります。お手元のnode.jsが20以降の場合はご注意ください。
ES Modules対応のJavaScriptを出力するよう、TypeScriptを設定します。
TypeScriptのWikiに、それぞれのnode.jsバージョンに合わせた推奨設定がまとめられています。
また@tsconfig/recommended
というパッケージにも、目的とする環境に合わせた推奨設定がまとめられています。ES Modulesをターゲットとした推奨設定もあります。
これらのサンプルをまとめると、ES Modules開発のためのtsconfig.jsonの設定は以下のようになります。
▼tsconfig.json
{
"compilerOptions": {
//必須の設定
"lib": ["ES2021"],
"module": "ES2022",
"target": "ES2021",
//オプション
"declaration": true
}
}
declarationは型定義ファイルを出力するか否かの指定です。必須ではありませんが、作業効率が向上するので出力しましょう。
モジュールがES Modules対応であることをpackage.jsonで明示します。
▼package.json
{
//必須の設定
"name": "あなたのパッケージの名前",
"main": "index.js",
"type": "module",
//オプション
"types": "index.d.ts",
}
mainフィールドはこのモジュールのエントリーポイントを指します。この指定が必須なのはES ModulesもCommonJSも変わりません。
typeフィールドは、拡張子js
のファイルをES Modulesファイル(.mjs)と扱うか、CommonJSファイル(.cjs)ファイルとして扱うかを指定します。module
を指定すると拡張子js
のファイルはmjs
ファイルとして扱われます。このフィールドを指定してもファイル名で.cjs
.mjs
を明示すれば、拡張子での指定が優先されます。
typesフィールドはTypeScriptの型定義ファイルを指定します。TypeScriptでES Modulesを開発するなら、作業効率が向上しますので指定しましょう。
type
とtypes
は見間違えそうになりますが、それぞれ別のフィールドです。ご注意ください。TypeScriptのimportでは拡張子を省略します。TypeScriptのimportは
ts
ファイルjs
ファイルd.ts
ファイルを区別なく読み込むためです。この方針は公式ドキュメントでも示されてきました。
しかし後発であるES Modulesのimportは、拡張子を指定するという方針を示しました。ここでES ModulesとTypeScriptの仕様に矛盾が生じました。
さまざまな解決方法が検討された結果、TypeScriptはES Modules対応のスクリプトを記述する時のみimport文に拡張子js
を明記するようになりました。tscはimportを変換しません。この方針はこのコメントで示されています。
▼ファイル構成
index.ts
ClassA.ts
tsconfig.json
このファイル構成でindex.tsからClassA.tsをimportする場合 ▼index.ts
import { ClassA } from "./ClassA.js"
^^
このように存在しないトランスパイル済みファイルClassA.js
ファイルを指定しなくてはいけません。
CommonJSにおいて、外部JSONファイルはrequire関数で簡単に読み込めました。しかし、ES Modulesにはrequire関数はありません。
const data = require("./data.json");
node.js v16において、実験的機能としてJSON modulesが実装されています。
import packageConfig from './package.json' assert { type: 'json' };
この機能は実験的なため、将来のバージョンでも使い続けられるかわかりません。
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const data = require("./data.json");
この問題を解決するのがcreateRequire関数です。require関数を生成して、外部jsonファイルを読み込みます。
以上、ありがとうございました。