404 Not Found

404 Not Found


nginx

TypeScriptのモジュールシステム

モジュールはコードを整理するための基本単位です。TypeScript は ES モジュールと CommonJS モジュールをサポートしており、さらにそれらに加えて型をエクスポートする機能も備えています。

1. ESモジュールの基礎

(1) エクスポートの命名

TYPESCRIPT
// utils.ts —— Name Export
export function add(a: number, b: number): number {
  return a + b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}

export const PI = 3.14159;
TYPESCRIPT
// main.ts —— Import by Name
import { add, multiply, PI } from "./utils";

console.log(add(1, 2));        // 3
console.log(multiply(3, 4));   // 12
console.log(PI);               // 3.14159

(2) デフォルトのエクスポート

TYPESCRIPT
// logger.ts —— Default Export
export default class Logger {
  constructor(private prefix: string) {}

  log(message: string): void {
    console.log(`[${this.prefix}] ${message}`);
  }
}
TYPESCRIPT
// main.ts —— Default Import(No curly braces needed,Custom Name)
import Logger from "./logger";
// That's fine, too.:import MyLogger from "./logger";

let logger = new Logger("APP");
logger.log("App Launch");  // [APP] App Launch

(3) デフォルトのエクスポートと名前付きエクスポートの両方の使用

TYPESCRIPT
// api.ts
export default class ApiClient {
  constructor(private baseUrl: string) {}

  async get(path: string): Promise<any> {
    // ...
  }
}

export enum HttpMethod {
  GET = "GET",
  POST = "POST",
  PUT = "PUT",
  DELETE = "DELETE"
}
TYPESCRIPT
// main.ts
import ApiClient, { HttpMethod } from "./api";

let client = new ApiClient("https://api.example.com");
let method: HttpMethod = HttpMethod.GET;

▶ 例:モジュラー式電卓

TYPESCRIPT
// calculator/operations.ts
export function add(a: number, b: number): number { return a + b; }
export function subtract(a: number, b: number): number { return a - b; }
export function multiply(a: number, b: number): number { return a * b; }
export function divide(a: number, b: number): number {
  if (b === 0) throw new Error("The divisor cannot be zero.");
  return a / b;
}
TYPESCRIPT
// calculator/index.ts
export { add, subtract, multiply, divide } from "./operations";
export type { Operation } from "./types";

// Default Export——Calculator Category
import * as ops from "./operations";

export default class Calculator {
  compute(op: string, a: number, b: number): number {
    switch (op) {
      case "+": return ops.add(a, b);
      case "-": return ops.subtract(a, b);
      case "*": return ops.multiply(a, b);
      case "/": return ops.divide(a, b);
      default: throw new Error(`Unknown Operation:${op}`);
    }
  }
}

2. 型のエクスポート

TypeScript では、型を個別にエクスポートすることができます。これは、JavaScript のモジュールシステムには備わっていない機能です:

(1) type 修飾子

TYPESCRIPT
// types.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export type UserId = number;

export type UserRole = "admin" | "editor" | "viewer";

// Use export type to explicitly mark"Export Types Only"
export type { User as UserType };

(2) インポート時の「型」と「値」の区別

TYPESCRIPT
// main.ts
import { type User, type UserRole, createUser } from "./types";
//        ↑ type Modifier Notation"Import Types Only"——It will be erased after compilation.

// Equivalent Old Syntax
// import { User, UserRole } from "./types";  // May result in runtime imports

// Recommended New Syntax——Clearly Distinguish Between Type Import and Value Import
import type { User, UserRole } from "./types";
import { createUser } from "./types";
💡 なぜこの区別をするのか? import type インポートされた型はコンパイル後に完全に消去されるため、実行時に requireimport が呼び出されることはない。これは、型のみを使用するシナリオ(型アノテーションやインターフェースなど)において極めて重要であり、不要なモジュールの読み込みを防ぐことができる。

(3) インライン型のインポート

TYPESCRIPT
// Mixed Import——Values and Types
import { createUser, type User, type UserRole } from "./types";

// createUser is the value——Requirements for runtime
// User and UserRole is a type——Compile-Time Erasure

3. 再エクスポートとバケットファイル

(1) 再輸出

TYPESCRIPT
// Re-exporting members of one module from another module
export { User, UserId } from "./user-types";
export { Product, ProductId } from "./product-types";
export { Order, OrderId } from "./order-types";

(2) バレルファイル

index.ts ディレクトリのエントリポイントとして、すべての公開APIを再エクスポートします:

TYPESCRIPT
// models/index.ts —— Bucket files
export { User, UserId } from "./user";
export { Product, ProductId } from "./product";
export { Order, OrderId } from "./order";
export type { CreateUser, UpdateUser } from "./user";
export type { CreateProduct, UpdateProduct } from "./product";
TYPESCRIPT
// Import directly from the directory when using it
import { User, Product, type CreateUser } from "./models";
// without needing to know which specific file it is in
💡 メリット: インポートパスを簡素化し、内部のファイル構造を隠蔽し、公開APIを制御できる。デメリット:不要なモジュールがインポートされる可能性がある(ツリーシェイクによってコードが完全に最適化されない場合がある)。


4. モジュールの解析戦略

TypeScript は、import "./utils" を実際のファイルにどのように解決すべきかを知る必要があります。これは、モジュール解決戦略によって決定されます。

(1) 2つの構文解析戦略

戦略 目的 説明
classic 旧バージョンとの互換性 まず .ts を検索し、次に .d.ts を検索
node (おすすめ) 最新のTSプロジェクト Node.jsの解析ロジックをシミュレート

(2) ノード解析戦略の検索順序

TEXT
import { X } from "./utils"

検索順序:

  1. ./utils.ts
  2. ./utils.tsx
  3. ./utils.d.ts
  4. ./utils/package.json 内の types フィールド
  5. ./utils/index.ts
  6. ./utils/index.d.ts

(3) node_modules での検索

TEXT
import _ from "lodash"

検索順序:

  1. ./node_modules/lodash.ts (存在しません)
  2. ./node_modules/lodash/package.jsontypes/typings フィールド
  3. ./node_modules/lodash/index.d.ts
  4. ./node_modules/@types/lodash/index.d.ts
  5. ../node_modules/../../node_modules/ … を検索する

5. パスマッピング(Path Mapping)

大規模なプロジェクトでは、長い相対パスを避けるためにパスエイリアスを使用します:

(1) tsconfig.json の設定

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

(2) パスエイリアスの使用

TYPESCRIPT
// No aliases——Relative paths are prone to errors
import { User } from "../../../models/user";
import { formatDate } from "../../utils/date";

// Has an alias——Clear and concise
import { User } from "@models/user";
import { formatDate } from "@utils/date";
⚠️ 注意: パスエイリアスはコンパイル時のマッピングに過ぎず、実行時(Node.js/ブラウザ)では @models のようなパスは認識されません。実行時のパス置換を行うには、ビルドツール(Webpackのresolve.alias、Viteのresolve.alias、またはtsc-aliasなど)を使用する必要があります。


6. CommonJS との相互運用性

(1) CommonJS モジュール

TYPESCRIPT
// Use CommonJS style export
// math.cjs
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
module.exports = { add, multiply };

(2) TSでのCommonJSのインポート

TYPESCRIPT
// esModuleInterop: false(Default)
import * as math from "./math.cjs";
math.add(1, 2);

// esModuleInterop: true(Recommendations)
import math from "./math.cjs";   // ✅ A More Natural Way to Introduce It
math.add(1, 2);

(3) TSからのCommonJSエクスポートを許可する

TYPESCRIPT
// Use export = syntax to export CommonJS Style
class Calculator {
  add(a: number, b: number): number { return a + b; }
}
export = Calculator;

// Use the following when importing: import = require
import Calculator = require("./calculator");
let calc = new Calculator();
💡 推奨事項: すべての新規プロジェクトでは ES モジュール (import/export) を使用し、レガシーな CommonJS パッケージとの互換性を維持するために esModuleInterop: true を有効にしてください。export = および import = は、CommonJS との厳密な互換性が求められる場合にのみ使用してください。


❓ よくある質問

Q import typeと通常のimportの違いは何ですか?
A import type この方法でインポートされた型は、コンパイル後に完全に消去され、実行時のモジュール読み込みは発生しません。通常の import ステートメントは値と型の両方をインポートし、コンパイル後に require() 呼び出しが発生します。型(インターフェースや型エイリアスなど)のみを使用する場合は、不要な実行時のインポートを避けるために、必ず import type を使用してください。
Q バケットファイル(index.ts)を使用すべきですか?
A ライブラリや公開APIでは、インポートを簡素化し、エクスポートされるインターフェースを管理するために推奨されます。内部向けアプリケーションの場合、状況によります。小規模なプロジェクトでは必ずしも必要ではありませんが、大規模なプロジェクトでは構成上の利点があります。主な欠点は、ツリーシェーキングに影響を与える可能性があることですが、最新のバンドラーではこの問題に対処できます。
Q 実行時にパスエイリアスはどのように機能するのですか?
A TypeScriptによって生成されたJSコードでは、依然としてエイリアスパス(@models/userなど)が使用されていますが、これらは実行時には認識されません。パスの置換はビルドツールによって処理される必要があります。Webpackではresolve.aliasが、Viteではresolve.aliasが使用され、純粋なtscコンパイルの場合はtsc-aliasのようなツールによる後処理が必要となります。
Q ESモジュールとCommonJS、どちらを使うべきですか?
A 新規プロジェクトでは、ESモジュールのみを使用してください (import/export)。CommonJSはNode.jsのレガシーなモジュールシステムであり、段階的に廃止されつつあります。TypeScriptのesModuleInteropオプションを使用すれば、CommonJSパッケージをシームレスに利用できます。ブラウザやDenoではESモジュールのみがサポートされています。

📖 まとめ

📝 練習問題

  1. 基本演習(難易度 ⭐):3つのモジュールファイル、math.ts(加算・減算をエクスポート)、string-utils.ts(大文字化・逆転をエクスポート)、index.ts(バケットファイルを再エクスポート)を作成してください。これらをmain.tsにインポートして使用してください。
  2. 上級演習(難易度 ⭐⭐):既存のプロジェクトのパスマッピングを設定します。@utilssrc/utils に、@modelssrc/models にマッピングしてください。import type を使用して型をインポートし、import を使用して値をインポートしてください。
  3. 課題(難易度:⭐⭐⭐):CommonJS パッケージ legacy-sdk を、ES Module 形式で TypeScript にインポートできるようにする宣言ファイル import LegacySDK from "legacy-sdk" を作成してください。esModuleInterop が有効な場合と無効な場合の両方を考慮してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%