TypeScript で配列をコピーするとき、浅いコピー(shallow copy)と深いコピー(deep copy)の違いを理解しておく必要がある。
浅いコピー
浅いコピーは配列の第一階層だけを複製する。プリミティブ値の配列であれば問題ないが、オブジェクトや配列を要素に持つ場合は参照が共有される。
const original = [1, 2, 3]; // スプレッド構文 const copy1 = [...original]; // Array.from const copy2 = Array.from(original); // slice const copy3 = original.slice();
スプレッド構文が最も簡潔で、現代の TypeScript では標準的な書き方である。Array.from は配列風オブジェクト(NodeList など)を配列に変換する用途でも使える。slice は引数なしで呼ぶと全要素のコピーを返す。
ネストした配列の問題
浅いコピーでは内部のオブジェクトや配列は同じ参照を指す。
const nested = [[1, 2], [3, 4]]; const shallowCopy = [...nested]; shallowCopy[0][0] = 100; console.log(nested[0][0]); // 100(元の配列も変わる)
内部配列 [1, 2] への参照がコピー先と共有されているため、片方を変更するともう片方にも影響する。
深いコピー
深いコピーはネストしたすべての階層を再帰的に複製する。
const nested = [[1, 2], [3, 4]]; // structuredClone(モダンな方法) const deepCopy = structuredClone(nested); deepCopy[0][0] = 100; console.log(nested[0][0]); // 1(元の配列は変わらない)
structuredClone は ES2022 以降のランタイムで利用でき、循環参照も正しく処理する。ただし関数や Symbol、DOM ノードなど一部の値はコピーできない。
古い環境や structuredClone が使えない場合は JSON.parse(JSON.stringify(obj)) という方法もあるが、undefined や関数、Date オブジェクトなどが正しく扱われない点に注意が必要である。
型付き配列のコピー
TypeScript では型情報がコピー先にも引き継がれる。
type User = { name: string; age: number }; const users: User[] = [ { name: "Alice", age: 30 }, { name: "Bob", age: 25 }, ]; const usersCopy: User[] = structuredClone(users);
コピー後の配列も User[] 型として扱われ、型安全性が維持される。