【TypeScript】import defer 完全ガイド — 評価を遅延する新構文

「Exploring TypeScript's import defer」の文字とモジュールの図を配したアイキャッチ画像

TypeScript 5.9のimport deferは、モジュールの読み込みと評価を分離し初回アクセスまで評価を遅らせる構文です。名前空間インポート限定のルールや動的importとの使い分け、nodenextでのエラーを確認できます。

目次

この記事が解決する悩み

  • 起動時に読み込まれるモジュールの評価を遅らせたい
  • import deferと動的importの使い分けが分からない

対象読者: TypeScriptでフロントエンドやNodeのコードを書く人
前提知識: ESMのimportを理解していること

結論

TypeScript 5.9のimport deferは、モジュールの読み込みと評価(トップレベル実行)を分離する構文です。静的importが読み込みと同時に評価されるのに対し、こちらはプロパティに初めてアクセスするまで評価を遅らせられます。

// 読み込みは即時、評価は初回アクセス時まで遅延
import defer * as analytics from "./analytics.js";

// この時点ではanalyticsモジュールはまだ評価されていない
console.log("App started");

// 初めてプロパティにアクセスした瞬間に評価される
analytics.track("page_view");

使えるのは名前空間インポート(import defer * as ns)だけで、名前付きimportやdefault importは書けません。

import deferの基本構文とルール

使える構文・使えない構文

制約は名前空間インポート限定の一点に集約されます。

// これだけが正しい
import defer * as feature from "./feature.js";

// 名前付きimportは不可
import defer { doSomething } from "./feature.js";

// default importも不可
import defer feature from "./feature.js";

// 名前空間なしも不可
import defer "./feature.js";

「プロパティアクセス時に評価をトリガーする」仕様なので、ns.valueのようにアクセスする名前空間オブジェクトでないと遅延の契機を取れないんですね。

TypeScriptのコンパイラ設定

import deferを使うにはmoduleオプションをesnextかpreserveに設定します。

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true
  }
}

TypeScriptはimport deferをdownlevelしません。そのままランタイムやバンドラに任せる設計で、nodenextなど他の設定ではコンパイルエラーになります。

静的import vs 動的import vs import defer

ES Modulesの3種類のimportを並べると、違いはこうなります。

特性静的import動的import import defer
解決(resolve)即時呼び出し時即時
取得・解析即時呼び出し時即時
評価(実行)即時Promise解決時初回プロパティアクセス時
同期/非同期同期非同期(Promise)同期
返値なし(静的宣言)PromiseModuleNamespace
コード分割不可可能読み込みは事前完了

重いモジュールを遅延読み込みするなら動的importが定番でしたが、非同期処理なので使う側もasync/awaitに巻き込まれます。

// 動的importの場合:非同期処理が必要
async function loadSettings() {
  const config = await import("./heavy-config.js");
  return config.default;
}

// import deferの場合:同期的に書ける
import defer * as config from "./heavy-config.js";

function loadSettings() {
  // ここで初めて評価される
  return config.default;
}

実践ユースケース

ユースケース1: 起動時間の改善

アプリ起動時に不要なモジュールの評価を遅らせるケースです。

// heavy-analytics.ts
console.log("Analytics module evaluating...");

const start = Date.now();
while (Date.now() - start < 500) {
  /* CPUバウンドな処理 */
}

export function track(event: string) {
  console.log("Track: " + event);
}

export const APP_VERSION = "1.0.0";

// main.ts
import defer * as analytics from "./heavy-analytics.js";

console.log("App started");
// 起動時間に500msのオーバーヘッドなし

button.addEventListener("click", () => {
  analytics.track("button_click");
});

静的importだと起動時にheavy-analyticsが評価されて500msブロックしますが、import deferならユーザーがボタンをクリックするまで評価されません。

ユースケース2: プラットフォーム固有のコード

実行環境で使うモジュールが変わる場合にも効きます。

// storage-web.ts
export function save(key: string, value: string) {
  localStorage.setItem(key, value);
}

// storage-node.ts
import { writeFileSync } from "node:fs";

export function save(key: string, value: string) {
  writeFileSync("./data/" + key + ".json", value);
}

// main.ts
import defer * as storage from "./storage-web.js";

export function savePreference(key: string, value: string) {
  storage.save(key, value);
}

起動時に両方を評価したり、動的importで非同期を強いられたりせず、初回使用時まで評価を遅延できます。

ユースケース3: 巨大な設定ファイルやi18nリソース

// i18n-resources.ts
export const resources = {
  ja: { /* ... 大量データ */ },
  en: { /* ... */ },
};

import defer * as i18n from "./i18n-resources.js";

function getMessage(locale: string, key: string): string {
  return i18n.resources[locale]?.[key] ?? "MISSING";
}

言語設定が変わったタイミングで初めてリソースが評価されるので、初期表示が高速になります。

import deferの注意点と制限

top-level awaitとの非互換

import deferで読み込んだモジュールや、その依存がtop-level awaitを使っている場合はすぐ評価されます。プロパティアクセスは同期でなければならず、非同期の評価は先に終わっている必要があるからです。

// async-module.ts
const response = await fetch("/config.json");
export const config = await response.json();

// main.ts
import defer * as cfg from "./async-module.js";
// top-level awaitを含むのでdeferしてもすぐに評価が開始される

ランタイムサポートの状況

import deferはECMAScriptのStage 3提案で、2026年9月時点でネイティブ対応するランタイムはありません。Chrome/V8チームが実装に興味を示している段階です。

バンドラの対応も進むでしょうから、今のうちに構文と使い方を押さえておいて損はありません。

読み込みは即時行われる

import deferが遅延するのは評価だけです。解決・取得・解析は静的importと同じタイミングなので、ネットワーク経由の遅延ローディングには従来通り動的importが必要です。

実測メモ: 型チェックの時点で --module の指定が効いてきます。TS 5.9.3 で確認したところ、nodenext だと error TS18060: Deferred imports are only supported when the '--module' flag is set to 'esnext' or 'preserve'. で弾かれ、esnext にすると通ります。ビルドツールの設定が nodenext のままの場合は、ここで一度ハマるので先に確認しておきましょう。

まとめ

import deferは静的importのシンプルさ(同期処理)を保ったまま、動的importのメリット(評価の遅延)を得られる構文ですね。

シチュエーションおすすめのimport方式
即時必要なモジュール静的import
条件によって読み込む(非同期OK)動的import
読み込みは事前、評価だけ遅延したい(同期のまま)import defer

「重い初期化処理を持つモジュール」「プラットフォーム固有の実装」「巨大な設定データ」を扱うプロジェクトなら、import deferを視野に入れた設計を始めてみてもいいかもしれませんね。

【広告】お名前.comならドメイン取得が格安。

検証環境

OS Windows 11 (build 10.0.26200)
CPU AMD Ryzen 9 PRO 8945HS w/ Radeon 780M Graphics
メモリ 28GB
言語/ツール TypeScript 5.9.3(npx経由) / Node.js 22.23.2
使用コマンド npx -p typescript@5.9.3 tsc –noEmit –module nodenext app.ts
測定日 2026-09-17

上記の環境で実際に実行して確認した結果です。環境が異なる場合は挙動が変わることがあります。

import defer を含むファイルを実際に型チェックした結果(構文が通るかを確認)
import defer を含むファイルを実際に型チェックした結果(構文が通るかを確認)

参考

本文の記述は以下の一次情報を確認して書いています。

あわせて読みたい

【広告】このサイトはConoHa WINGで運営しています。安定した高速サーバーで快適にブログを書けています。いつもありがとう!!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次