モジュール構文
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宣言への参照と並んで、名前付きエクスポートで参照することもできます。
ts
export { f, SomeType, SomeInterface };
エクスポートされた型(およびその他のTypeScript固有の宣言)は、標準のECMAScriptインポートでインポートできます。
ts
import { f, SomeType, SomeInterface } from "./module.js";
名前空間のインポートまたはエクスポートを使用する場合、エクスポートされた型は、型位置で参照されるときに名前空間で利用できます。
ts
import * 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には存在しないため、発行しない位置でのみ使用できます。
ts
import 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
を名前付きバインディングとして使用します。
ts
import 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"
のような、実行時によって提供されるモジュールを表します。
ts
declare 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;}
アンビエントモジュールは、モジュール宣言の本体内でインポートを使用して、包含するファイルをモジュールに変換することなく(アンビエントモジュール宣言をモジュール拡張にする)、他のモジュールを参照することができます。
ts
declare module "m" {// Moving this outside "m" would totally change the meaning of the file!import { SomeType } from "other";export function f(): SomeType;}
パターンアンビエントモジュールは、その名前に単一の*
ワイルドカード文字を含み、インポートパス内の0個以上の文字に一致します。これは、カスタムローダーによって提供されるモジュールを宣言するのに役立ちます。
ts
declare 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("...")
に対して特別な変換があります。
ts
import x = require("mod");
js
import { createRequire as _createRequire } from "module";const __require = _createRequire(import.meta.url);const x = __require("mod");
CommonJS出力は--module commonjs
と似ていますが、動的なimport()
呼び出しは変換されません。ここで表示される出力は、esModuleInterop
が有効になっている状態です。
ts
import 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モジュールですが、依存関係は任意の形式にすることができます。
例
ts
import 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
import 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
で表示されます。
ts
import 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";
ts
import 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モジュールローダーでの使用を想定しています。
例
ts
import 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
System.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
をサポートします。
例
ts
import 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
define(["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モジュールですが、依存関係は任意の形式にすることができます。
例
ts
import 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
ステートメントの文字列リテラル)をディスク上のファイルに解決する方法を制御し、ターゲットランタイムまたはバンドラーで使用されるモジュールリゾルバーと一致するように設定する必要があります。