Skip to content

Jest環境での「ReferenceError: structuredClone is not defined」エラーの解決策

問題の概要

Node.jsとTypeScript環境でJestを使用したテスト実行中にReferenceError: structuredClone is not definedエラーが発生する問題です。このエラーは、structuredClone()関数がJestテスト環境で認識されない場合に発生します。具体的には次のような条件で問題が現れます:

  • structuredClone()をテストコード内で使用
  • Node.jsのバージョンがv17未満の場合
  • Jestテスト環境(jsdomなど)がstructuredCloneをサポートしていない場合
typescript
// 問題が発生する典型的なコード例
const variableForValidation = structuredClone(variableForValidationUncloned);

このエラーはコンパイル時には検出されず、実際にテスト実行時にのみ発生するため、開発者を混乱させる原因となります。

重要な背景情報

structuredClone()関数はNode.js v17.0.0(2021年10月リリース)で追加された機能です。このバージョン以前のNode環境や、Jestのテストランナー環境(特にjsdom)ではネイティブサポートされていません。

解決方法一覧

1. Node.jsのバージョンアップ(推奨)

最も直接的な解決策は、Node.jsをv17以上に更新することです。

bash
# Node.jsのバージョンを確認
$ node --version

# v17以上が表示されない場合、バージョンアップが必要

バージョンアップ後、package.jsonのenginesフィールドで最小バージョンを指定すると、将来のバージョン問題を予防できます。

json
{
  "engines": {
    "node": ">=17.0.0"
  }
}

2. Jestテスト環境でのモック実装

Node.jsのバージョンアップが不可能な場合、structuredCloneのポリフィル(代替実装)をテスト環境に追加します。

typescript
// すべてのテスト前に実行されるセットアップファイル
global.structuredClone = (val: unknown): any => 
  JSON.parse(JSON.stringify(val));
javascript
module.exports = {
  setupFiles: ['<rootDir>/globalSetup.ts'],
  testEnvironment: 'jsdom', // または 'node'
  // 他の設定...
};

JSONメソッド使用の制限点

JSON.parse(JSON.stringify())はシンプルですが、次のデータ型には対応していません:

  • 循環参照を含むオブジェクト
  • Map/Set/Date/RegExpなどの組み込み型
  • 関数やundefined
  • ArrayBufferなどのバイナリデータ

3. 強化されたポリフィルの導入

より複雑なデータ型をサポートする必要がある場合は、専門ライブラリを使用します。

bash
# 循環参照対応のポリフィルをインストール
npm install @ungap/structured-clone --save-dev
npm install @types/ungap__structured-clone --save-dev
typescript
// テストファイルでインポート
import structuredClone from '@ungap/structured-clone';

// 使用例
test('複雑な構造複製', () => {
  const original = new Map([['key', 'value']]);
  const cloned = structuredClone(original);
  expect(cloned.get('key')).toBe('value');
});

4. Jest環境の設定見直し

Jest自体のバージョンやテスト環境設定が原因の場合もあります。

typescript
// テスト内で実行環境を確認
test('Nodeバージョン確認', () => {
  console.log(`実行Nodeバージョン: ${process.version}`);
  console.log(`Jestバージョン: ${jest.version}`);
});
bash
実行Nodeバージョン: v16.14.0
Jestバージョン: 28.1.0

トラブルシューティングステップ

  1. Jestとts-jestを最新版に更新
  2. IDE/ターミナルで使用するNodeバージョンを統一
  3. Jest設定のtestEnvironmentを正しく設定(ブラウザAPI使用時はjsdom

5. Vitestへの移行(代替案)

Jestの制限が問題解決の妨げになる場合、Vitestへの移行を検討します。VitestはViteベースの互換性のあるテストランナーです。

Vitest移行手順
bash
# Jest関連パッケージの削除
npm remove jest ts-jest @types/jest jest-environment-jsdom

# VitestとDOM環境のインストール
npm install -D vitest happy-dom @vitest/coverage-istanbul

# テストファイルの変更
// すべてのテストファイル先頭に追加
import { describe, test, expect } from 'vitest';

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'happy-dom',
    globals: true,
    setupFiles: './setupTests.ts' // 必要なセットアップがあれば
  }
});

各解決策の比較表

方法必要な作業量持続可能性対応データ型パフォーマンス
Node.jsバージョンアップ全てのネイティブ型最適
モック実装基本型のみ対応良好
ポリフィル導入拡張型に対応環境依存
Vitest移行最新標準対応非常に高い

ベストプラクティス

  1. プロジェクト開始時にNode.jsバージョンを固定 (.nvmrcenginesで指定)
  2. Dockerを使用してテスト環境統一:バージョン差異を完全排除
  3. 重要な構造の複製には専用ユーティリティ関数を実装
  4. TypeScriptのtarget設定を定期的に見直し(es2022以降を推奨)
typescript
// 安全なオブジェクト複製関数の例
export function safeClone<T>(obj: T): T {
  if (typeof structuredClone === 'function') {
    return structuredClone(obj);
  }
  // structuredCloneがない環境のフォールバック
  try {
    return JSON.parse(JSON.stringify(obj));
  } catch (error) {
    console.error('Clone failed', error);
    return {...obj}; // シャローコピーで代替
  }
}

参考リソース

structuredCloneは深いコピーを行う現代的な方法ですが、テスト環境の制約を正しく理解し、プロジェクト要件に合った適切な解決策を選択することが重要です。