背景
Chakra UI v3 を使ったプロジェクトで Jest のテストを実行したら ReferenceError: structuredClone is not defined で落ちた。Chakra UI v3 が内部で structuredClone を使っているが、Jest の jsdom テスト環境では Node.js ネイティブの structuredClone がグローバルに公開されないことが原因だった
そもそも structuredClone って?、というところから調べたのでまとめる
JavaScript のコピーには3段階ある
そもそもJSのコピーには下記の3種類がある
| 方法 | コピーの深さ | 特徴 |
|---|---|---|
=(代入) |
コピーしていない(参照共有) | 同じデータを別名で使いたいだけの時 |
{ ...obj } / [...arr](スプレッド構文) |
浅い(1階層目のみ) | フラットなオブジェクトのコピー |
structuredClone(obj) |
深い(全階層) | ネストしたデータの完全な複製 |
代入は「参照の共有」であってコピーではない
const original = { name: "太郎", scores: [80, 90] };
const copy = original;
copy.name = "花子";
console.log(original.name); // "花子" ← 元も変わる
スプレッド構文は1階層目しかコピーしない(シャローコピー)
const original = { name: "太郎", scores: [80, 90] };
const copy = { ...original };
copy.scores.push(100);
console.log(original.scores); // [80, 90, 100] ← ネストした配列は共有されたまま
structuredClone は全階層を完全に複製する(ディープコピー)
const original = { name: "太郎", scores: [80, 90] };
const copy = structuredClone(original);
copy.scores.push(100);
console.log(original.scores); // [80, 90] ← 元は変わらない
structuredClone は JSON.parse/stringify の上位互換
以前のディープコピーの定番だった JSON.parse(JSON.stringify(obj)) と比較すると:
| 型 | JSON.parse/stringify |
structuredClone |
|---|---|---|
Date |
文字列になって壊れる | 正しくコピーされる |
Map / Set
|
消える | 正しくコピーされる |
undefined |
消える | 正しくコピーされる |
RegExp |
空オブジェクト {} になる |
正しくコピーされる |
| 循環参照 | エラーで落ちる | 正しくコピーされる |
Jest で structuredClone is not defined になる問題と解決策
Jest の jsdom テスト環境では Node.js ネイティブの structuredClone がグローバルに公開されない。そのため Chakra UI v3 のようにこれを使うライブラリがあるとテストが落ちる
対処として jest.setup.ts に JSON.parse/stringify ベースの簡易ポリフィルを追加して解決した
// jest.setup.ts に追加したポリフィル
if (typeof globalThis.structuredClone === "undefined") {
globalThis.structuredClone = <T>(val: T): T =>
JSON.parse(JSON.stringify(val));
}
Chakra UI 内部ではプレーンなオブジェクトしかクローンしないため、JSON 方式の弱点(Date, Map 等の非対応)には当たらない。だからこの簡易ポリフィルで十分
まとめ
- JavaScript のコピーは「参照共有」「シャローコピー」「ディープコピー」の3段階がある
-
structuredCloneはJSON.parse/stringifyの上位互換で、Date, Map, 循環参照なども正しくコピーできる - Jest の
jsdom環境ではstructuredCloneがグローバルに公開されないので、ポリフィルが必要
感想
- スプレッド構文の性質については初めて知った、びっくり