エラーの内容
クリーンなデフォルトインポートを書いたとたん、TypeScript がすぐに警告を出します:
Module 'express' can only be default-imported using the 'allowSyntheticDefaultImports' flag
Module 'react' can only be default-imported using the 'allowSyntheticDefaultImports' flag
Module 'lodash' can only be default-imported using the 'allowSyntheticDefaultImports' flag
このエラーは、新しい npm パッケージを追加したとき、TypeScript の設定を厳しくしたとき、または既存の JS プロジェクトを TypeScript に移行したときに発生しやすいです。
原因
express、lodash、moment などの古い CommonJS パッケージには、本物の ES モジュールのデフォルトエクスポートがありません。これらは module.exports = ... を通じてすべてを公開しています。TypeScript はこの不一致を検出し、次のような書き方を許可しません:
import express from 'express'; // ❌ フラグなしではエラー
allowSyntheticDefaultImports フラグは、TypeScript が「module.exports をデフォルトエクスポートとして扱う」ことを許可するための設定です。これは型チェックのみの設定であり、コンパイル後の JavaScript 出力はまったく変わりません。
知っておくべき重要な点として、esModuleInterop を有効にすると、allowSyntheticDefaultImports も自動的に有効になります。詳細は修正方法 2 で説明します。
修正方法 1 — tsconfig.json で allowSyntheticDefaultImports を有効にする(推奨)
tsconfig.json を開き、compilerOptions にフラグを追加します:
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
// ... その他のオプション
}
}
保存して tsc を再実行してください。エラーが消えます。この1行の変更で、大多数のプロジェクトの問題が解決します。
修正方法 2 — esModuleInterop を使用する(Node.js/CommonJS プロジェクトにより適した方法)
esModuleInterop はアップグレード版と考えてください。allowSyntheticDefaultImports が行うすべてのことに加え、出力された JavaScript で CommonJS の相互運用を正しく機能させるランタイムヘルパーも注入します。Node.js プロジェクトや CommonJS パッケージをバンドルする場合は、こちらを使用することをお勧めします:
{
"compilerOptions": {
"esModuleInterop": true,
// esModuleInterop が true の場合、allowSyntheticDefaultImports も自動的に true になります
}
}
これを設定すると、デフォルトインポートが正常に動作します:
import express from 'express'; // ✅
import React from 'react'; // ✅
import _ from 'lodash'; // ✅
import moment from 'moment'; // ✅
修正方法 3 — 名前空間インポートを使用する(tsconfig を変更せずに)
TypeScript の設定を変更できない場合は、代わりに名前空間インポートを使用してください:
// 以下の代わりに:
import express from 'express'; // ❌
// こちらを使用:
import * as express from 'express'; // ✅
これが機能するのは、モジュールが何かをエクスポートしているからです — ただし、名前付きデフォルトとしてではありません。名前空間インポートは module.exports オブジェクト全体を取得します。ただし注意点として、見た目が少し奇妙であり、一部のライブラリの型定義はデフォルトインポート構文を前提としています。この不一致により、コードの他の場所で二次的な型エラーが発生する可能性があります。
修正の確認
コンパイラを実行してすべてが正常であることを確認します:
# tsc を直接使用する場合
npx tsc --noEmit
# ビルドスクリプトがある場合
npm run build
Vite または CRA プロジェクトの場合は、ファイルを保存するだけです。開発サーバーが自動的に再コンパイルし、ブラウザの赤いエラーバナーは1〜2秒以内に消えるはずです。
現在有効なフラグを確認したい場合は、次のコマンドを実行します:
npx tsc --showConfig | grep -E 'allowSyntheticDefaultImports|esModuleInterop'
次のように表示されるはずです:
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
よくあるシナリオ:Create React App / Vite
CRA と Vite はどちらも、生成された tsconfig.json にデフォルトで esModuleInterop: true が含まれています。そのため、新規作成した CRA または Vite プロジェクトでこのエラーが発生する場合は、生成後に何かが変更されています。フラグが手動で削除されたか、それを上書きするベース設定を継承しているかのいずれかです。両方のファイルを確認してください:
{
"extends": "./tsconfig.base.json", // ← このファイルも確認
"compilerOptions": { ... }
}
よくあるシナリオ:JavaScript からの移行
.js ファイルを .ts にリネームして TypeScript を初めて起動した場合、最初のコンパイル時にこのエラーが発生します。express、lodash、moment などのパッケージがすべてこのエラーを引き起こします。移行設定の一環として tsconfig.json に esModuleInterop: true を追加してください — これにより、すべての問題を一度に解決できます。
クイックヒント
allowSyntheticDefaultImports: true単独よりもesModuleInterop: trueを優先してください — CommonJS の相互運用においてより安全であり、TypeScript チームの推奨でもあります。- 他者が使用するライブラリを作成している場合は、
esModuleInteropの使用に注意してください。これは出力された JavaScript でのインポートの処理方法を変更するため、同じ設定を使用していないユーザーが驚くことがあります。 - 一部のパッケージ(
@types/nodeを含む)は、適切な ES デフォルトエクスポートを持つように更新されています。メンテナンスが行き届いたパッケージでもこのエラーが発生する場合、原因はほぼ確実に型定義ではなく tsconfig にあります。 - 同じモジュールに対して名前空間インポート(
import * as X from '...')とデフォルトインポート(import X from '...')を混在させないでください — どちらか一方のスタイルを選び、ファイル全体で統一してください。

