モジュール構文
TypeScriptコンパイラは、TypeScriptファイルおよびJavaScriptファイルで標準のECMAScriptモジュール構文を認識し、JavaScriptファイルでさまざまな形式のCommonJS構文を認識します。
TypeScriptファイルやJSDocコメントで使用できる、いくつかのTypeScript固有の構文拡張もあります。
TypeScript固有の宣言のインポートとエクスポート
型エイリアス、インターフェース、列挙型、名前空間は、標準のJavaScript宣言と同様に、export修飾子を使用してモジュールからエクスポートできます。
ts// Standard JavaScript syntax...export function f() {}// ...extended to type declarationsexport type SomeType = /* ... */;export interface SomeInterface { /* ... */ }
標準のJavaScript宣言への参照と並んで、名前付きエクスポートで参照することもできます。
tsexport { f, SomeType, SomeInterface };
エクスポートされた型(およびその他のTypeScript固有の宣言)は、標準のECMAScriptインポートでインポートできます。
tsimport { f, SomeType, SomeInterface } from "./module.js";
名前空間のインポートまたはエクスポートを使用する場合、エクスポートされた型は、型位置で参照されるときに名前空間で利用できます。
tsimport * as mod from "./module.js";mod.f();mod.SomeType; // Property 'SomeType' does not exist on type 'typeof import("./module.js")'let x: mod.SomeType; // Ok
型のみのインポートとエクスポート
JavaScriptへのインポートとエクスポートを発行するとき、デフォルトでは、TypeScriptは型位置でのみ使用されるインポートと、型のみを参照するエクスポートを自動的に削除(発行しない)します。型のみのインポートとエクスポートを使用して、この動作を強制し、削除を明示的にすることができます。import typeで記述されたインポート宣言、export type { ... }で記述されたエクスポート宣言、およびtypeキーワードがプレフィックスとして付いたインポートまたはエクスポート指定子はすべて、出力JavaScriptから削除されることが保証されています。
ts// @Filename: main.tsimport { f, type SomeInterface } from "./module.js";import type { SomeType } from "./module.js";class C implements SomeInterface {constructor(p: SomeType) {f();}}export type { C };// @Filename: main.jsimport { f } from "./module.js";class C {constructor(p) {f();}}
値でさえimport typeでインポートできますが、出力JavaScriptには存在しないため、発行しない位置でのみ使用できます。
tsimport type { f } from "./module.js";f(); // 'f' cannot be used as a value because it was imported using 'import type'let otherFunction: typeof f = () => {}; // Ok
型のみのインポート宣言は、デフォルトインポートと名前付きバインディングの両方を宣言することはできません。typeがデフォルトインポートに適用されるか、インポート宣言全体に適用されるかが曖昧になるためです。代わりに、インポート宣言を2つに分割するか、defaultを名前付きバインディングとして使用します。
tsimport type fs, { BigIntOptions } from "fs";// ^^^^^^^^^^^^^^^^^^^^^// Error: A type-only import can specify a default import or named bindings, but not both.import type { default as fs, BigIntOptions } from "fs"; // Ok
import()型
TypeScriptは、インポート宣言を記述せずにモジュールの型を参照するための、JavaScriptの動的なimportに似た型構文を提供します。
ts// Access an exported type:type WriteFileOptions = import("fs").WriteFileOptions;// Access the type of an exported value:type WriteFileFunction = typeof import("fs").writeFile;
これは、型をインポートすることができないJavaScriptファイルのJSDocコメントで特に役立ちます。
ts/** @type {import("webpack").Configuration} */module.exports = {// ...}
export = と import = require()
CommonJSモジュールを発行するとき、TypeScriptファイルはmodule.exports = ...とconst mod = require("...") JavaScript構文の直接のアナログを使用できます。
ts// @Filename: main.tsimport fs = require("fs");export = fs.readFileSync("...");// @Filename: main.js"use strict";const fs = require("fs");module.exports = fs.readFileSync("...");
変数宣言とプロパティの代入はTypeScriptの型を参照できなかったため、特別なTypeScript構文を使用できたため、この構文はJavaScriptの対応物よりも優先して使用されていました。
ts// @Filename: a.tsinterface Options { /* ... */ }module.exports = Options; // Error: 'Options' only refers to a type, but is being used as a value here.export = Options; // Ok// @Filename: b.tsconst Options = require("./a");const options: Options = { /* ... */ }; // Error: 'Options' refers to a value, but is being used as a type here.// @Filename: c.tsimport Options = require("./a");const options: Options = { /* ... */ }; // Ok
アンビエントモジュール
TypeScriptは、スクリプト(非モジュール)ファイルにおいて、実行時に存在し、対応するファイルがないモジュールを宣言するための構文をサポートしています。これらのアンビエントモジュールは通常、Node.jsにおける"fs"や"path"のような、実行時によって提供されるモジュールを表します。
tsdeclare module "path" {export function normalize(p: string): string;export function join(...paths: any[]): string;export var sep: string;}
アンビエントモジュールがTypeScriptプログラムにロードされると、TypeScriptは他のファイルで宣言されたモジュールのインポートを認識します。
ts// 👇 Ensure the ambient module is loaded -// may be unnecessary if path.d.ts is included// by the project tsconfig.json somehow./// <reference path="path.d.ts" />import { normalize, join } from "path";
アンビエントモジュールの宣言は、モジュール拡張と構文が同一のため、混同しやすいです。このモジュール宣言の構文は、ファイルがモジュールの場合、つまりトップレベルにimportまたはexportステートメントがある場合(または--moduleDetection forceまたはautoの影響を受ける場合)、モジュール拡張になります。
ts// Not an ambient module declaration anymore!export {};declare module "path" {export function normalize(p: string): string;export function join(...paths: any[]): string;export var sep: string;}
アンビエントモジュールは、モジュール宣言の本体内でインポートを使用して、包含するファイルをモジュールに変換することなく(アンビエントモジュール宣言をモジュール拡張にする)、他のモジュールを参照することができます。
tsdeclare module "m" {// Moving this outside "m" would totally change the meaning of the file!import { SomeType } from "other";export function f(): SomeType;}
パターンアンビエントモジュールは、その名前に単一の*ワイルドカード文字を含み、インポートパス内の0個以上の文字に一致します。これは、カスタムローダーによって提供されるモジュールを宣言するのに役立ちます。
tsdeclare module "*.html" {const content: string;export default content;}
moduleコンパイラオプション
このセクションでは、各moduleコンパイラオプションの値の詳細について説明します。オプションの内容と、コンパイルプロセス全体における位置づけについては、モジュール出力形式の理論セクションを参照してください。簡単に言えば、moduleコンパイラオプションは、歴史的には、出力されるJavaScriptファイルのモジュール形式を制御するためにのみ使用されていました。しかし、最近のnode16およびnodenextの値は、Node.jsのモジュールシステムの幅広い特性、サポートされているモジュール形式、各ファイルのモジュール形式の決定方法、異なるモジュール形式が相互運用する方法などを記述しています。
node16, nodenext
Node.jsは、CommonJSとECMAScriptモジュールの両方をサポートしており、各ファイルがどの形式になるか、および2つの形式がどのように相互運用できるかについての特定のルールがあります。node16とnodenextは、Node.jsのデュアル形式モジュールシステムのすべての動作を記述しており、CommonJSまたはESM形式のいずれかでファイルを出力します。これは、実行環境に依存せず、すべての出力ファイルを単一の形式に強制し、出力が実行環境に対して有効であることをユーザーが保証する必要がある他のすべてのmoduleオプションとは異なります。
よくある誤解は、
node16とnodenextがESモジュールのみを出力するということです。実際には、node16とnodenextは、ESモジュールを使用するプロジェクトだけでなく、ESモジュールをサポートするNode.jsのバージョンを記述しています。各ファイルの検出されたモジュール形式に基づいて、ESMとCommonJSの両方の出力がサポートされています。node16とnodenextは、Node.jsのデュアルモジュールシステムの複雑さを反映する唯一のmoduleオプションであるため、ESモジュールを使用するかどうかにかかわらず、Node.js v12以降で実行することを目的としたすべてのアプリとライブラリにとって、唯一正しいmoduleオプションです。
node16とnodenextは現在同一であり、異なるtargetオプション値を暗示するという例外があります。Node.jsが将来モジュールシステムに大幅な変更を加えた場合、node16は凍結され、nodenextは新しい動作を反映するように更新されます。
モジュール形式の検出
.mts/.mjs/.d.mtsファイルは常にESモジュールです。.cts/.cjs/.d.ctsファイルは常にCommonJSモジュールです。.ts/.tsx/.js/.jsx/.d.tsファイルは、最も近い祖先のpackage.jsonファイルに"type": "module"が含まれている場合はESモジュール、それ以外の場合はCommonJSモジュールです。
入力された.ts/.tsx/.mts/.ctsファイルの検出されたモジュール形式によって、出力されるJavaScriptファイルのモジュール形式が決まります。たとえば、.tsファイルのみで構成されるプロジェクトは、--module nodenextの場合、デフォルトですべてCommonJSモジュールを出力し、プロジェクトのpackage.jsonに"type": "module"を追加することで、すべてESモジュールを出力させることができます。
相互運用性のルール
- ESモジュールがCommonJSモジュールを参照する場合
- CommonJSモジュールの
module.exportsは、ESモジュールへのデフォルトインポートとして利用できます。 - CommonJSモジュールの
module.exportsのプロパティ(default以外)は、ESモジュールへの名前付きインポートとして利用できる場合とできない場合があります。Node.jsは、静的解析によってそれらを利用できるように試みます。TypeScriptは、宣言ファイルからその静的解析が成功するかどうかを知ることができず、成功すると楽観的に仮定します。これにより、TypeScriptは実行時にクラッシュする可能性のある名前付きインポートをキャッチする能力が制限されます。詳細については、#54018を参照してください。
- CommonJSモジュールの
- CommonJSモジュールがESモジュールを参照する場合
requireはESモジュールを参照できません。TypeScriptの場合、これは検出されたCommonJSモジュールであるファイル内のimportステートメントを含みます。これらのimportステートメントは、出力されるJavaScriptでrequire呼び出しに変換されるためです。- 動的な
import()呼び出しを使用して、ESモジュールをインポートできます。これは、モジュールのモジュール名前空間オブジェクト(別のESモジュールからのimport * as ns from "./module.js"から取得できるもの)のPromiseを返します。
出力
各ファイルの出力形式は、各ファイルの検出されたモジュール形式によって決まります。ESM出力は--module esnextと似ていますが、--module esnextでは許可されていないimport x = require("...")に対して特別な変換があります。
tsimport x = require("mod");
jsimport { createRequire as _createRequire } from "module";const __require = _createRequire(import.meta.url);const x = __require("mod");
CommonJS出力は--module commonjsと似ていますが、動的なimport()呼び出しは変換されません。ここで表示される出力は、esModuleInteropが有効になっている状態です。
tsimport fs from "fs"; // transformedconst dynamic = import("mod"); // not transformed
js"use strict";var __importDefault = (this && this.__importDefault) || function (mod) {return (mod && mod.__esModule) ? mod : { "default": mod };};Object.defineProperty(exports, "__esModule", { value: true });const fs_1 = __importDefault(require("fs")); // transformedconst dynamic = import("mod"); // not transformed
暗黙的および強制的なオプション
--module nodenextまたはnode16は、同じ名前でmoduleResolutionを暗黙的に指定し、強制します。--module nodenextは、--target esnextを暗黙的に指定します。--module node16は、--target es2022を暗黙的に指定します。--module nodenextまたはnode16は、--esModuleInteropを暗黙的に指定します。
まとめ
node16とnodenextは、ESモジュールを使用するかどうかにかかわらず、Node.js v12以降で実行することを目的としたすべてのアプリとライブラリにとって、唯一正しいmoduleオプションです。node16とnodenextは、各ファイルの検出されたモジュール形式に基づいて、CommonJSまたはESM形式のいずれかでファイルを出力します。- ESMとCJS間のNode.jsの相互運用性ルールは、型チェックに反映されます。
- ESM出力は、
import x = require("...")を、createRequireインポートから構築されたrequire呼び出しに変換します。 - CommonJS出力は、動的な
import()呼び出しを変換せずに残すため、CommonJSモジュールはESモジュールを非同期的にインポートできます。
es2015, es2020, es2022, esnext
概要
- バンドラー、Bun、tsxでは、
--moduleResolution bundlerと一緒にesnextを使用してください。 - Node.jsには使用しないでください。Node.js用のESモジュールを出力するには、package.jsonで
"type": "module"とともにnode16またはnodenextを使用してください。 import mod = require("mod")は、宣言ファイル以外では許可されていません。es2020は、import.metaプロパティのサポートを追加します。es2022は、トップレベルのawaitのサポートを追加します。esnextは、ECMAScriptモジュールへのStage 3提案のサポートを含める可能性のある、動きのあるターゲットです。- 出力されるファイルはESモジュールですが、依存関係は任意の形式にすることができます。
例
tsimport x, { y, z } from "mod";import * as mod from "mod";const dynamic = import("mod");console.log(x, y, z, mod, dynamic);export const e1 = 0;export default "default export";
jsimport x, { y, z } from "mod";import * as mod from "mod";const dynamic = import("mod");console.log(x, y, z, mod, dynamic);export const e1 = 0;export default "default export";
commonjs
概要
- おそらくこれを使用すべきではありません。Node.js用のCommonJSモジュールを出力するには、
node16またはnodenextを使用してください。 - 出力されるファイルはCommonJSモジュールですが、依存関係は任意の形式にすることができます。
- 動的な
import()は、require()呼び出しのPromiseに変換されます。 esModuleInteropは、デフォルトおよび名前空間インポートの出力コードに影響します。
例
出力は
esModuleInterop: falseで表示されます。
tsimport x, { y, z } from "mod";import * as mod from "mod";const dynamic = import("mod");console.log(x, y, z, mod, dynamic);export const e1 = 0;export default "default export";
js"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.e1 = void 0;const mod_1 = require("mod");const mod = require("mod");const dynamic = Promise.resolve().then(() => require("mod"));console.log(mod_1.default, mod_1.y, mod_1.z, mod);exports.e1 = 0;exports.default = "default export";
tsimport mod = require("mod");console.log(mod);export = {p1: true,p2: false};
js"use strict";const mod = require("mod");console.log(mod);module.exports = {p1: true,p2: false};
system
概要
- SystemJSモジュールローダーでの使用を想定しています。
例
tsimport x, { y, z } from "mod";import * as mod from "mod";const dynamic = import("mod");console.log(x, y, z, mod, dynamic);export const e1 = 0;export default "default export";
jsSystem.register(["mod"], function (exports_1, context_1) {"use strict";var mod_1, mod, dynamic, e1;var __moduleName = context_1 && context_1.id;return {setters: [function (mod_1_1) {mod_1 = mod_1_1;mod = mod_1_1;}],execute: function () {dynamic = context_1.import("mod");console.log(mod_1.default, mod_1.y, mod_1.z, mod, dynamic);exports_1("e1", e1 = 0);exports_1("default", "default export");}};});
amd
概要
- RequireJSのようなAMDローダー用に設計されています。
- おそらくこれを使用すべきではありません。代わりにバンドラーを使用してください。
- 出力されるファイルはAMDモジュールですが、依存関係は任意の形式にすることができます。
outFileをサポートします。
例
tsimport x, { y, z } from "mod";import * as mod from "mod";const dynamic = import("mod");console.log(x, y, z, mod, dynamic);export const e1 = 0;export default "default export";
jsdefine(["require", "exports", "mod", "mod"], function (require, exports, mod_1, mod) {"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.e1 = void 0;const dynamic = new Promise((resolve_1, reject_1) => { require(["mod"], resolve_1, reject_1); });console.log(mod_1.default, mod_1.y, mod_1.z, mod, dynamic);exports.e1 = 0;exports.default = "default export";});
umd
概要
- AMDまたはCommonJSローダー用に設計されています。
- 他のほとんどのUMDラッパーのようにグローバル変数を公開しません。
- おそらくこれを使用すべきではありません。代わりにバンドラーを使用してください。
- 出力されるファイルはUMDモジュールですが、依存関係は任意の形式にすることができます。
例
tsimport x, { y, z } from "mod";import * as mod from "mod";const dynamic = import("mod");console.log(x, y, z, mod, dynamic);export const e1 = 0;export default "default export";
js(function (factory) {if (typeof module === "object" && typeof module.exports === "object") {var v = factory(require, exports);if (v !== undefined) module.exports = v;}else if (typeof define === "function" && define.amd) {define(["require", "exports", "mod", "mod"], factory);}})(function (require, exports) {"use strict";var __syncRequire = typeof module === "object" && typeof module.exports === "object";Object.defineProperty(exports, "__esModule", { value: true });exports.e1 = void 0;const mod_1 = require("mod");const mod = require("mod");const dynamic = __syncRequire ? Promise.resolve().then(() => require("mod")) : new Promise((resolve_1, reject_1) => { require(["mod"], resolve_1, reject_1); });console.log(mod_1.default, mod_1.y, mod_1.z, mod, dynamic);exports.e1 = 0;exports.default = "default export";});
moduleResolutionコンパイラーオプション
このセクションでは、複数のmoduleResolutionモードで共有されるモジュール解決の機能とプロセスについて説明し、次に各モードの詳細を指定します。オプションの内容とコンパイルプロセス全体での適合性に関する背景については、モジュール解決の理論セクションを参照してください。簡単に言うと、moduleResolutionは、TypeScriptがモジュール指定子(import/export/requireステートメントの文字列リテラル)をディスク上のファイルに解決する方法を制御し、ターゲットランタイムまたはバンドラーで使用されるモジュールリゾルバーと一致するように設定する必要があります。