「Cannot use import statement outside a module」というエラーメッセージは、JavaScriptまたはTypeScriptモジュール内で設定を誤った状態でimportを使おうとすると表示されます。

JavaScriptのサーバーサイドランタイム環境のひとつであるNode.jsについて言えば、CommonJSモジュールシステムのrequireキーワードが想定される場面でimport(ECMAScript(ES)のもの)を使用するとこのエラーが発生します。

TypeScriptは異なるモジュール形式をサポートしているものの、モジュールのインポートに関するESとCommonJSのアプローチを混同したコーディングエラーも原因になります。

また、クライアントサイド(ブラウザ)では、JavaScriptコードファイルにバンドラーを使用していない場合などに発生します。

この3つの原因について掘り下げ、それぞれの状況でエラーを解決する方法をご紹介します。

サーバーサイドJavaScriptでエラーを解決する方法

まずは、サーバーサイドJavaScript環境で「Cannot use import statement outside a module」エラーを解決する方法を見ていきましょう。

原因

Node.jsは、デフォルトでCommonJSシステムのrequireキーワードを使用するため、ESモジュールをサポートするように設定しなければ、今回のエラーが表示されます。また、ESモジュールを認識し動作させるには、.mjs拡張子が必要になります。

解決方法

.mjsを使用する代わりに、バンドラーを使用するか、--experimental-modulesフラグを付けてNode.jsを実行することで、古いバージョンのNode.jsに現在のESモジュールとの互換性を持たせることができます。あるいは、以下のようにpackage.jsonファイルのtypeフィールドをmoduleに設定してください。

{
  "name": "test-package",
  "version": "1.0.0",
  "type": "module",
  "main": "app.js",
  "dependencies": { }
}

typeプロパティは、すべてのパッケージのpackage.jsonファイルに含めてください。これによって、使用中のモジュールシステムを識別しやすくなり、ライブラリ間の一貫性が確保されます。

エラーを回避する別の方法は、importexportの構文が正しく、適切に読み込まれるようにすることです。常に相対ファイルパス、名前付き(named)エクスポート、エクスポート用のファイル拡張子を使用し、デフォルト(default)エクスポートを避けましょう。

この例は以下のとおりです。

//モジュールをインポート
import { sampleFunction } from './sampleModule.js';

// 関数をエクスポート
export function sampleFunction() {
     // コードがここに表示される
}

最後に、すべてのサードパーティライブラリとESモジュールの互換性を確認します。これについては、package.jsonファイルのライブラリのドキュメントをご覧ください。または、バンドラーを使用してコードをトランスパイルし、JavaScript環境がコードを理解できるようにします。

TypeScript環境でエラーを解決する方法

続いて、TypeScript環境でエラーを解決する方法をご紹介します。

原因

モジュールを使用することで、プロジェクト内の複数のファイル間でコードを再利用、整理、共有することができます。ESはimportexportキーワードを使用し、さまざまなファイル間でコードを共有するための外部モジュールをサポートしています。

通常は、TypeScript環境でESモジュールを使用するように設定されていないことが原因でエラーが発生します。TypeScriptはJavaScriptのスーパーセットであるため、インポートにはデフォルトでCommonJS構文が使用され、importではなくrequireが使用されます。この場合、importステートメントがエラーを引き起こします。ESモジュールをサポートするには、TypeScriptを正しく設定する必要があります。

誤ったファイル拡張子を使用している場合も同様のエラーを引き起こします。例えば、ESモジュールを持つNode.js環境でTypeScriptを使用する場合、インポートするモジュールのファイル拡張子は、通常の.jsでなく.mjsでなければなりません。

Webpackのようなバンドラーを使用する場合、tsconfig.jsonpackage.jsonファイルのmoduleフィールドの設定が不適切であることも、よくみられる原因です。この場合は、tsconfig.jsonファイルのmoduletargetフィールドをECMAScriptに設定することで、ESモジュール用のバンドラーを使用できます。これによって、Webpackはターゲット環境を理解し、コードをトランスパイルする際に正しいファイル拡張子を使用するようになります。

解決方法

RequireJSのようなモジュールローダーや、Webpackのようなバンドラーを使用してESモジュールを読み込むには、tsconfig.jsonファイルに以下の貼り付けます。

{
  "compilerOptions": {
    "module": "es20215",
    "target": "es20215",
    "sourceMap": true
  }
}

上記コードのcompilerOptions部分で、moduletargetフィールドがes20215モジュールを使用するように設定します。これでimportexportの記述を使用してもエラーが出なくなります。

TypeScriptはデフォルトでCommonJSを使用するため、tsconfig.jsonファイルを変更しないと、エラーメッセージが表示されます。

幸い、moduletargetフィールドがECMAScriptモジュールを使用するように設定した後は、exportを使ってモジュールから関数や変数をエクスポートし、importで別のモジュールを現在のモジュールのスコープに読み込むことができます。このコードは以下の通りです。

// sum.ts
export function sum(a: number, b: number, c: number): number {
  return a + b + c;
}

// main.ts
import { sum } from './sum';
console.log(add(4, 4, 9));

古いバージョンのNode.jsをお使いの場合は、--experimental-modulesを付けてコードを実行することで、ESモジュールのサポートを有効にできます。また、Webpack、Browserify、Rollupのようなバンドラーを使用して、すべてのESコードをバンドルし、単一のファイルに出力してください。ブラウザや古いバージョンのNode.jsが理解できるバージョンであることを確認し、プロジェクトのルートにモジュールタイプを指定するWebpack.config.jsファイルを設定します。

以下は、Webpackの公式ドキュメントから抜粋した例です。

module.exports = {
  entry: './src/index.ts',
  output: {
    filename: 'bundle.js',
   path: path.resolve(__dirname, 'dist')
  },
  resolve: {
    extensions: ['.ts', '.js', '.mjs']
  },
  module: {
    rules: [
      {
        test: /.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  experiments: {
    outputModule: true
  }
};

コンパイルされたコードは、プロジェクトのルートディレクトリのdistディレクトリにあるbundle.jsファイルに出力されます。

また、es-module-shimsのようなPolyfill(ポリフィル)を使用して、ESモジュールのimportexportをサポートしていない古いブラウザをターゲットにすることも可能です。

クライアントサイドJavaScriptでエラーを解決する方法

最後に、クライアントサイドのJavaScript環境でエラーを解決する方法を見ていきましょう。

原因

Chrome、Firefox、Edge、Safariなど、ほぼすべてのモダンブラウザは、ESモジュールをサポートしているため、Polyfill、バンドラー、トランスパイラを使用する必要はありません。

また、ReactやVueのJavaScriptベースのフロントエンドライブラリを使う場合も、デフォルトでESのimportsexportsフィールドがサポートされているため不要です。ただし、古いブラウザはES構文をサポートしていないため、クロスプラットフォームの互換性を考慮し、これらのツールが必要になります。

古いブラウザでエラーが発生する最も一般的な原因は、ページのHTMLファイルにtype="module"属性が含まれていないこと。このシナリオでは、ウェブ上で実行されるJavaScriptがESモジュールをデフォルトでサポートしていないため、エラーが発生します。また、ワイヤーを介して送信されるJavaScriptコードでは、異なるドメインからESモジュールを読み込もうとすると、クロスオリジンリソース共有(CORS)エラーが発生することがあります。

解決方法

古いブラウザでエラーを回避するには、HTMLファイルで正しいscriptタグ属性(type="module")を使用します。または、古いブラウザでも理解できるように、Webpackを使用してコードをトランスパイルします。

type="module"属性を使用するには、HTMLファイルに次の行を追加してください。

<script type="module" src="app.js"></script>

また、importファイルのパスが有効であること、正しいimportを使用していることも必ず確認してください。

Can I Useなどのサイトで、ESモジュールのブラウザ互換性をチェックすることができます。

.jsファイル拡張子を使用するのは一般的な方法であるため、回避策として、モジュールのHTMLファイルのscriptタグにtype属性を設定することができます。この属性をmoduleに設定すると、ブラウザは拡張子.jsを無視し、ファイルをモジュールとして扱うようになります。

まとめ

「Cannot use import statement outside a module」エラーは、クライアントサイド(ブラウザ上)のJavaScript環境、またはサーバサイドのJavaScript環境で発生します。これには、構文や設定のミス、サポートされていないファイル拡張子など、様々な原因が考えられます。

ほとんどのモダンブラウザはESモジュールをサポートしていますが、古いブラウザでは互換性を確保しなければなりません。Webpackのようなバンドラーを使用すると、依存関係を持つすべてのソースコードを理解可能な単一の出力にコンパイルできます。

モジュールがESモジュールであることをブラウザに知らせるためには、HTMLファイルにtype="module"属性を追加してください。また、CommonJSには.js拡張子を使うのがデフォルトですが、.mjs拡張子を使えばESモジュールをインポート可能です。

JavaScriptアプリケーションを稼働したいけど、サーバー管理は面倒?Kinstaのアプリケーションホスティングデータベースホスティングがお役に立てるはずです。静的サイトホスティングと組み合わせれば、無料でアプリケーションのフロントエンドを提供することも可能です。