TypeScriptモジュール入門!ESModulesとCommonJSの違いを初心者向けに徹底解説
生徒
「TypeScriptでプログラムを作っていると、ファイルがどんどん長くなって読みづらくなってきました。ファイルを分ける良い方法はありますか?」
先生
「そんな時は『モジュール』という仕組みを使います。大きなプログラムを部品ごとに小分けにして、必要な時に呼び出すことができるんですよ。」
生徒
「なるほど!部品化ですね。でも、調べるとESModulesとかCommonJSとか難しい言葉が出てきて混乱しています…。」
先生
「大丈夫ですよ。それらは部品をやり取りするための『ルール』の種類のことです。それでは、基本的な使い方と違いを見ていきましょう!」
1. モジュールとは?
TypeScriptやプログラミングにおける「モジュール」とは、一言で言うと「プログラムの部品化」のことです。例えば、料理のレシピ本を想像してみてください。一冊の本に「カレー」「肉じゃが」「サラダ」の作り方が全部書いてあると、ページを探すのが大変ですよね。これを「カレーの章」「サラダの章」というふうに、独立したファイルに分けて管理するのがモジュールの考え方です。
なぜモジュールが必要なのでしょうか?それは、プログラムが大規模になると、一つのファイルに何千行もコードが書かれることになり、どこに何が書いてあるか分からなくなるからです。モジュール化することで、以下のようなメリットがあります。
- 整理整頓: 関連する機能をまとめて、別のファイルに保存できる。
- 再利用: 一度作った便利な機能を、他のプログラムでも使い回せる。
- 名前の衝突を防ぐ: 違うファイルであれば、同じ名前の変数を使っても混ざらない。
プログラミング未経験の方にとって、「ファイルを分ける」というのは当たり前のことに感じるかもしれませんが、コンピューターに対して「別のファイルにあるこれを使ってね!」と命令するには、決まったルールが必要になります。それが今回学習する内容です。
2. モジュールの基本操作:exportとimport
TypeScriptでモジュールを扱う際に、必ず覚える必要があるキーワードが2つあります。それがexport(エクスポート)とimport(インポート)です。
日常の言葉に例えると、exportは「出荷」、importは「輸入」です。あるファイルで作った部品を「外に出す」のがexportで、その部品を別のファイルで「取り込む」のがimportです。
export:部品を公開する
他のファイルから使いたい変数や関数(命令のセット)の前に export と書くだけで、その部品は「公開」状態になります。例えば、計算を行う関数を公開してみましょう。
// mathUtils.ts というファイル名だとします
export const add = (a: number, b: number) => {
return a + b;
};
export const subtract = (a: number, b: number) => {
return a - b;
};
import:部品を読み込む
公開された部品を別のファイルで使いたいときは、import を使います。どのファイルの、どの部品が欲しいのかを指定します。
// main.ts というファイルから読み込みます
import { add, subtract } from "./mathUtils";
console.log(add(10, 5));
console.log(subtract(10, 5));
実行結果は以下のようになります。
15
5
このように、必要な機能だけを摘まみ取って持ってくることができるのがモジュールの素晴らしいところです。なお、./mathUtils の ./ は「同じフォルダ内にある」という意味の記号です。パソコンに慣れていない方は、住所を指定しているようなものだと考えてください。
3. モジュールシステムの種類(ESModulesとCommonJS)
さて、ここからが少し難しいポイントです。TypeScript(およびJavaScript)には、モジュールを実現するための「規格」が大きく分けて2つあります。それが ESModules (ESM) と CommonJS (CJS) です。
なぜ2つもあるのでしょうか?それは、歴史的な背景があるからです。昔のJavaScriptにはモジュールの仕組みがありませんでした。そこで「サーバー側で動かすために必要だ!」として作られたのが CommonJS で、後に「JavaScriptの公式ルールとして決めよう!」として作られたのが ESModules です。
| 特徴 | ESModules (ESM) | CommonJS (CJS) |
|---|---|---|
| 標準 | 現在の標準ルール(推奨) | 昔からのルール(Node.jsで主流) |
| 書き方 | import / export | require / module.exports |
| 動作環境 | ブラウザ、最新のNode.js | 古いNode.js、サーバー環境 |
ESModules(エス・モジュール)
現在の主流であり、TypeScriptで開発する際に最もよく使われる形式です。公式の標準規格なので、これから新しくプログラムを学ぶならこちらが基本になります。先ほど紹介した import と export を使う書き方そのものです。
CommonJS(コモン・ジェーエス)
Node.jsという、パソコン上でJavaScriptを動かす仕組みで長く使われてきた形式です。古いプロジェクトや、特定のライブラリ(便利な道具セット)を使う際には今でも目にすることがあります。
書き方は以下のようになります。
// CommonJS形式でのエクスポート(出荷)
const multiply = (a: number, b: number) => a * b;
module.exports = multiply;
// CommonJS形式でのインポート(輸入)
const multiply = require("./multiply");
TypeScriptを使っている場合、基本的には ESModules の書き方(import/export)でコードを書き、設定ファイル(tsconfig.json)で最終的にどちらの形式に出力するかを調整するのが一般的です。初心者の方は、「今は ESModules が標準なんだな」と覚えておけば十分です。
4. 名前空間(Namespace)とは?
TypeScriptには、モジュールと似た機能で 名前空間(Namespace) というものがあります。以前は「内部モジュール」と呼ばれていました。
モジュールが「ファイルを分ける」ことで整理するのに対し、名前空間は「一つのファイル内や、複数のファイルにまたがって、名前のグループを作る」ために使われます。例えば、「動物園」というグループの中に「ライオン」という名前を登録するようなイメージです。そうすることで、別の「サファリパーク」グループに「ライオン」がいても、お互いに混ざらなくて済みます。
名前空間の書き方
namespace というキーワードを使って、その中に機能を閉じ込めます。外から使わせたいものには export を付けます。
namespace Zoo {
export class Lion {
say() {
console.log("ガオー!");
}
}
}
// 使うときは「グループ名.名前」で指定する
const myLion = new Zoo.Lion();
myLion.say();
注意点: 現在のモダンなTypeScript開発では、この「名前空間」よりも、最初にご紹介した「モジュール(ファイルを分ける方法)」を使うことが強く推奨されています。名前空間は、非常に古いプログラムの整理や、特定の特殊な状況以外ではあまり使われなくなっています。まずは「モジュール」をマスターすることに専念しましょう。
5. デフォルトエクスポート(default export)
モジュールには、もう一つの便利な出し方があります。それが デフォルトエクスポート です。一つのファイルから「これがメインの部品ですよ!」と一つだけ指定して送り出す方法です。
default export の例
エクスポートする際に default と付けると、インポートする側で { }(波括弧)を使わずに、好きな名前で受け取ることができます。
// Message.ts
const message = "こんにちは、世界!";
export default message;
// main.ts
import myMessage from "./Message"; // 好きな名前(myMessage)で受け取れる
console.log(myMessage);
通常のエクスポート(名前付きエクスポート)は、一つのファイルにたくさんの部品がある場合に適しており、デフォルトエクスポートは、一つのファイルが一つの大きな役割を持っている場合に適しています。どちらを使うかはチームのルールによりますが、初心者のうちは「名前付きエクスポート(波括弧を使う方)」を使ったほうが、どの部品を使っているか明確になるのでおすすめです。
6. 複雑なフォルダ構成での読み込み
ファイルが増えてくると、フォルダの中にフォルダを作って管理することがあります。これを「階層(かいそう)構造」と呼びます。例えば、「料理」フォルダの中に「和食」フォルダがあり、その中に「お米.ts」がある場合です。
パソコンを初めて触る方は少し戸惑うかもしれませんが、以下のルールを覚えておきましょう。
./: 今いる場所(同じフォルダ内)../: 一つ上の階層(フォルダの外に出る)
例えば、一つ上のフォルダにある config.ts を読み込みたいときは、以下のように書きます。
import { settings } from "../config";
このように、ファイルの場所を正確に指定することで、どんなに大きなプロジェクトになっても迷子にならずにプログラムを組み立てることができます。モジュールシステムは、まさに巨大なレゴブロックの城を作るための、設計図のような役割を果たしているのです。