カテゴリ: TypeScript 更新日: 2026/01/31

TypeScriptとNode.jsでのモジュール解決の仕組みを理解する!初心者向け完全ガイド

TypeScriptとNode.jsでのモジュール解決の仕組みを理解する
TypeScriptとNode.jsでのモジュール解決の仕組みを理解する

先生と生徒の会話形式で理解しよう

生徒

「TypeScriptで他のファイルからコードを読み込むとき、どうやって探してくるんですか?」

先生

「それはモジュール解決という仕組みで行われています。TypeScriptとNode.jsが連携して、必要なファイルを見つけ出すんですよ。」

生徒

「モジュール解決って何ですか?難しそうですね…」

先生

「大丈夫です。図書館で本を探すのと同じような仕組みなので、順番に見ていきましょう!」

1. モジュール解決とは?

1. モジュール解決とは?
1. モジュール解決とは?

モジュール解決とは、TypeScriptやNode.jsがimportやrequireで指定されたファイルやパッケージを探し出す仕組みのことです。プログラミングでは、複数のファイルに分けてコードを書くことが一般的です。そのとき、あるファイルから別のファイルを読み込む必要がありますが、コンピュータはどこにそのファイルがあるのかを探さなければなりません。

例えば、図書館で本を探すとき、書名や著者名から本棚の場所を特定しますよね。それと同じように、TypeScriptとNode.jsは特定のルールに従って、必要なファイルやパッケージがどこにあるのかを探し出します。この探索のプロセスをモジュール解決(Module Resolution)と呼びます。

2. TypeScriptのモジュール解決の基本

2. TypeScriptのモジュール解決の基本
2. TypeScriptのモジュール解決の基本

TypeScriptでは、主に2つのモジュール解決方式があります。それがClassicNodeです。現在のTypeScript開発では、ほとんどの場合Node方式が使われています。これはNode.jsの動作に合わせた解決方法で、より実用的だからです。

モジュール解決方式の指定

TypeScriptの設定ファイルであるtsconfig.jsonで、どの解決方式を使うかを指定できます。


{
  "compilerOptions": {
    "moduleResolution": "node"
  }
}

この設定で、Node.js互換のモジュール解決を使用することを宣言しています。moduleResolutionという項目が、モジュールをどのように探すかを決める設定です。

3. 相対パスと非相対パスの違い

3. 相対パスと非相対パスの違い
3. 相対パスと非相対パスの違い

モジュールを読み込むとき、パスの書き方によって探し方が変わります。大きく分けて相対パス非相対パスの2種類があります。

相対パスによるインポート

相対パスとは、現在のファイルから見た相対的な位置でファイルを指定する方法です。./../で始まるパスが相対パスです。


import { User } from './models/user';
import { Config } from '../config/settings';

./は現在のフォルダ、../は1つ上のフォルダを意味します。この方法は、自分で作成したファイルを読み込むときによく使います。

非相対パスによるインポート

非相対パスは、./../で始まらないパスです。これは主に外部パッケージを読み込むときに使います。


import express from 'express';
import { readFile } from 'fs';

このような書き方をすると、TypeScriptとNode.jsはnode_modulesフォルダの中からパッケージを探します。

4. Node.jsスタイルのモジュール解決の仕組み

4. Node.jsスタイルのモジュール解決の仕組み
4. Node.jsスタイルのモジュール解決の仕組み

Node.jsスタイルの解決方法では、非相対パスのモジュールを探すとき、以下の順番で探索が行われます。

探索の順序

  1. 現在のフォルダのnode_modulesを確認
  2. 見つからなければ、親フォルダのnode_modulesを確認
  3. さらに上の階層のnode_modulesを順に確認
  4. ルートディレクトリまで到達しても見つからなければエラー

例えば、/project/src/app.tsというファイルでimport lodash from 'lodash';と書いた場合、以下の順番で探します。

  • /project/src/node_modules/lodash
  • /project/node_modules/lodash
  • /node_modules/lodash

通常、プロジェクトのルートにあるnode_modulesにパッケージがインストールされているため、そこで見つかります。

5. ファイル拡張子の解決

5. ファイル拡張子の解決
5. ファイル拡張子の解決

TypeScriptは、インポート時にファイルの拡張子を省略できます。拡張子が省略された場合、TypeScriptは自動的に複数の拡張子を試して、該当するファイルを探します。


import { helper } from './utils/helper';

この場合、TypeScriptは以下の順番でファイルを探します。

探索される拡張子の順序
  1. helper.ts
  2. helper.tsx
  3. helper.d.ts

.d.tsは型定義ファイルと呼ばれ、JavaScriptライブラリにTypeScriptの型情報を追加するためのファイルです。

6. index.tsによるディレクトリのインポート

6. index.tsによるディレクトリのインポート
6. index.tsによるディレクトリのインポート

フォルダを指定してインポートすると、そのフォルダ内のindex.tsというファイルが自動的に読み込まれます。これはNode.jsの規則をTypeScriptも引き継いでいます。


import { functions } from './utils';

この場合、実際には./utils/index.tsが読み込まれます。これにより、複数のファイルをまとめて公開するときに便利です。

具体例:utilsフォルダの構成

以下のようなフォルダ構成があるとします。


utils/
  ├── index.ts
  ├── math.ts
  └── string.ts

index.tsで他のファイルをまとめて公開すれば、外部からはutilsだけを指定して使えます。


// utils/index.ts
export * from './math';
export * from './string';

7. package.jsonのmainフィールド

7. package.jsonのmainフィールド
7. package.jsonのmainフィールド

パッケージをインポートするとき、そのパッケージのルートにあるpackage.jsonmainフィールドが確認されます。これはパッケージのエントリーポイント(入口)を示します。


{
  "name": "my-library",
  "version": "1.0.0",
  "main": "dist/index.js"
}

このパッケージをimport myLibrary from 'my-library';でインポートすると、実際にはdist/index.jsが読み込まれます。TypeScriptの場合、typesフィールドで型定義ファイルの場所も指定できます。


{
  "name": "my-library",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts"
}

8. baseUrlとpathsの活用

8. baseUrlとpathsの活用
8. baseUrlとpathsの活用

プロジェクトが大きくなると、相対パスが長くなって管理が大変になります。TypeScriptではbaseUrlpathsを使って、独自のパス解決ルールを設定できます。


{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@models/*": ["models/*"],
      "@utils/*": ["utils/*"]
    }
  }
}

この設定により、以下のように短く書けるようになります。


// 設定前
import { User } from '../../../models/user';

// 設定後
import { User } from '@models/user';

@記号を使ったパスは、エイリアスと呼ばれ、コードの可読性を高めます。

9. 実践例:モジュール解決の動作確認

9. 実践例:モジュール解決の動作確認
9. 実践例:モジュール解決の動作確認

実際にモジュール解決がどのように動作するか、簡単な例で確認してみましょう。以下のようなプロジェクト構成を考えます。


project/
  ├── src/
  │   ├── app.ts
  │   └── services/
  │       └── user.ts
  ├── node_modules/
  │   └── express/
  └── tsconfig.json

app.tsで様々なインポートを試してみます。


// 相対パスでの自作モジュール
import { UserService } from './services/user';

// 非相対パスでの外部パッケージ
import express from 'express';

// TypeScriptが探索する順序
// './services/user' → src/services/user.ts を直接参照
// 'express' → node_modules/express を探索

TypeScriptコンパイラは、それぞれのインポート文に対して適切な解決方法を自動的に選択し、ファイルを見つけ出します。もし見つからなければ、コンパイル時にエラーメッセージが表示されます。

10. トラブルシューティング:モジュールが見つからないとき

10. トラブルシューティング:モジュールが見つからないとき
10. トラブルシューティング:モジュールが見つからないとき

開発中によく遭遇する「モジュールが見つかりません」というエラーの対処法を紹介します。

よくあるエラー

Cannot find module './services/user' or its corresponding type declarations.

チェックポイント

  • ファイルパスの確認:スペルミスや大文字小文字の間違いがないか
  • 拡張子の確認:ファイルが実際に存在するか
  • node_modulesの確認:パッケージがインストールされているか
  • tsconfig.jsonの確認:moduleResolutionの設定は正しいか

特に外部パッケージの場合、npm installyarn installでパッケージをインストールし忘れていることがよくあります。エラーメッセージをよく読んで、何が見つからないのかを確認しましょう。

関連記事:
カテゴリの一覧へ
新着記事
New1
TypeScript
TypeScriptでパスエイリアスを設定する方法!baseUrlとpathsでコードをスッキリ整理
New2
JavaScript
JavaScriptのfor文の書き方を初心者向けにやさしく解説
New3
JavaScript
JavaScriptの関数でよくあるエラーとその解決法まとめ
New4
JavaScript
JavaScriptのイベント処理でよくあるエラーとその対処法
人気記事
No.1
Java&Spring記事人気No1
JavaScript
JavaScriptのインストール方法まとめ!Windows・Mac・Linux別にステップ解説
No.2
Java&Spring記事人気No2
JavaScript
JavaScriptのマウスイベントの使い方(click, mouseoverなど)
No.3
Java&Spring記事人気No3
JavaScript
JavaScriptのtoStringとString関数の違いを初心者向けに解説
No.4
Java&Spring記事人気No4
JavaScript
JavaScriptの純粋関数(pure function)と副作用の違いを理解しよう
No.5
Java&Spring記事人気No5
JavaScript
JavaScriptプログラムの実行方法まとめ!ブラウザ・Node.js・コンソールの使い方
No.6
Java&Spring記事人気No6
JavaScript
JavaScriptで文字列をforループで1文字ずつ処理する方法!初心者向け解説
No.7
Java&Spring記事人気No7
TypeScript
TypeScript学習におすすめの無料教材・リファレンスサイト【初心者向け】
No.8
Java&Spring記事人気No8
JavaScript
JavaScriptのDOMContentLoadedとloadイベントの違いを理解しよう