第10章:モジュール(import/export)で迷わない🌿

🎯この章のゴール
この章が終わったら、こんな感じになれるよ〜!🥰
- ファイルを分割しても
import/exportで迷わない✨ - 「なんで読み込めないの!?😵💫」系エラーを、落ち着いて直せる🔧
- “設定を増やさずに” まず動く形を作れる✅
📚まずは超ざっくり:モジュールって何?
**モジュール=「別ファイルにある機能を、importして使えるしくみ」**だよ📦
TypeScriptのモジュールは基本 ESM(ECMAScript Modules) の import/export で考えるのが今どきの勝ち筋✨ (nodejs.org)
🧭結論:迷わないための“2つの型”だけ覚えよ💡
実は詰まりポイントは「実行環境のルールの違い」から来ることが多いの…!😿
✅A)テスト/ビルドが“Vite/Vitest寄り(バンドラ寄り)”の型
- 体感:importがわりと自由でラク✨
- VitestはVite powered だよ🧪⚡ (vitest.dev)
✅B)Node.jsで“そのまま実行(ESM)”の型
- 体感:拡張子
.jsを書けなど、ルールが厳しめ⚠️ - NodeのESMは 相対importに拡張子が必須(例:
./a.js) (nodejs.org) - ESMにする方法(例:
"type": "module")もNode側のルール (nodejs.org)
この教材の流れ(次章でVitest)だと、まずは Aの型を基準にすると安定しやすいよ🫶 ただしBに行くときの罠もこの章で“地雷処理”しておくね💣✨
🧪基本フォーム:export / import(ここだけ固定でOK)
① “名前付きexport”が基本(おすすめ)🌟
// src/math/add.ts
export function add(a: number, b: number) {
return a + b;
}
(名前付きは後で増えても整理しやすいよ🧺)
// src/main.ts
import { add } from "./math/add";
console.log(add(2, 3));
{ add }は「名前が add のものを使うよ」って意味✅
② default export は“使うなら徹底”🎁
// src/math/add.ts
export default function add(a: number, b: number) {
return a + b;
}
// src/main.ts
import add from "./math/add";
- default は 名前を自由に変えられるから、チームだとブレやすいこともあるよ😵💫
- 迷ったら 名前付きexport でOK🙆♀️
③ “型だけimport”は、はっきり書くと強い🛡️
TypeScriptは「型のためだけのimport」を自動で消してくれるけど、明示すると事故が減るよ✨
import type は 出力JSに残らない(=実行時には存在しない) って約束🧠 (typescriptlang.org)
import type { UserId } from "./types";
import { add } from "./math/add";
- 「型だけのつもりが、実行時importになって副作用が…😱」を避けやすい!
🧱“モジュール解決”で詰まらないための最小知識
ここが詰まりポイントの正体だよ〜!🕵️♀️
✅ TypeScriptの module と moduleResolution はセットで動く
TypeScriptは module の値によって、moduleResolution のデフォルトが変わるよ📌 (typescriptlang.org)
たとえば module: "preserve" だと moduleResolution: "bundler" が基本、みたいな感じ✨ (typescriptlang.org)
🧪手を動かす:1ファイル→2ファイル→“整理import”まで(15分コース)⏱️💞
Step 1:まず分ける(最小)
// src/price/calcTax.ts
export function calcTaxIncluded(price: number, rate: number) {
return Math.floor(price * (1 + rate));
}
// src/main.ts
import { calcTaxIncluded } from "./price/calcTax";
console.log(calcTaxIncluded(1000, 0.1)); // 1100
✅ ここで「読み込めた!」の感覚を作るのが大事💓
Step 2:フォルダに“窓口(index)”を作る(ちょい整理)🪟
// src/price/index.ts
export { calcTaxIncluded } from "./calcTax";
// src/main.ts
import { calcTaxIncluded } from "./price";
もし
./priceが解決されなくて詰まったら、無理せず./price/indexにしてOK🙆♀️ (環境の解決ルールで「フォルダ=index」が効かないことがあるの🌀)
Step 3:型チェックだけ先に回す(安心✨)
ビルドや実行の前に、まずこれだけでOK:
npx tsc --noEmit
- importパスのミスとかもここで見つかるよ🔍
💥あるある詰まりポイント辞典(ここが本題😎✨)
1) Cannot find module './xxx' 😭
✅ まず見る順番:
- スペル(大文字/小文字も!)
- 相対パスの基準(今いるファイルから見てる?)
- エイリアス(paths/baseUrl)を使ってない?
Vitest/Viteは
tsconfigのpathsをデフォで見ないことがあるよ〜! (vitest.dev)
2) Nodeで ERR_MODULE_NOT_FOUND / 拡張子のせい?😵
NodeのESMは 相対importに拡張子が必要(例:./a.js) (nodejs.org)
TypeScriptのドキュメント例も、.ts から ./module.js をimportする形で書かれてたりするよ📘 (typescriptlang.org)
🔧対処の考え方(どれか選ぶ)
- Node直実行なら、importに
.jsを書く流儀に寄せる - もしくはビルド結果に合わせて、TypeScript側で拡張子を書き換える設定を検討
(例:
rewriteRelativeImportExtensionsというオプションがあるよ) (typescriptlang.org)
3) exports is not defined in ES module scope 😨
CommonJS(module.exports)っぽいコードが混ざってるサインかも⚠️
NodeのESMは import/export 前提で動くよ📦 (nodejs.org)
4) default export がない / named export がない 🤯
import add from← default前提import { add } from← named前提 ここがズレると即死する😂 ✅ プロジェクト内のルールを1個に統一しよ〜!
5) 型だけのimportが実行時に悪さしてる気がする👻
import type を使って「型は型!」って明示すると安定するよ🛡️ (typescriptlang.org)
🤖AIの使い方(この章はここが強い✨)
AIには“設定を盛る”方向に走らせず、原因当てゲームをさせるのがコツ🎯
🧾おすすめプロンプト(コピペOK)
- 「このエラーは ①パスミス ②拡張子問題 ③module/moduleResolution問題 ④default/named不一致 のどれ?理由つきで一個に絞って」
- 「最小の修正で直すならどれ?“設定追加なし案”→“設定追加あり案”の順で出して」
- 「この
importが 実行時に必要? 型だけ?判定してimport typeに直して」
✅チェック(できたら合格っ💕)
-
exportとimportを “named中心” で書けた✨ - ファイル分割しても、importが迷子にならない🧭
- 1回は「読み込めない😭」を経験して、直せた🔧
-
import typeを1回使った🛡️
🌈次章につながる一言
次の第11章でVitestを入れると、**import周りの違和感(特にpaths/alias)**が一気に出やすいの!🧪 だからこの章で「詰まりポイントの分類」ができたの、めちゃ強いよ💪✨