🧊
【Typescript】再帰的 Object.freeze
概要
Object.freeze
を使うことでオブジェクトの既存のプロパティを編集不可能にすることができます.
ただし, Object.freeze
はオブジェクトのネストされたプロパティには影響を与えないので, 再帰的に実行する必要があります.
オブジェクトのネストされたプロパティを再帰的に freeze し、変更不能にするための関数を作成しました.
実装
freeze できるのは基本的に obj === null || (typeof obj !== "object" && typeof obj !== "function")
を満たすものです.
そのため, 関数内でチェックを行います.
ブラウザや Node.js などの実行環境によって提供されるホストオブジェクト(例えば、ブラウザの window
オブジェクトや DOM 要素)は freeze しようとするとエラーになる場合があります.
そのため, try-catch を入れて Object.freeze
のエラーは握り潰しています.
また, プロパティの循環参照がある場合に無限ループが発生するため, その対策も入れています.
export const deepFreeze = <T>(obj: T, depth = Infinity): Readonly<T> => {
// If obj is null (a kind of object), undefined, primitive, or already frozen, return as it is
if (
obj === null ||
(typeof obj !== "object" && typeof obj !== "function") ||
Object.isFrozen(obj)
) {
return obj;
}
if (depth <= 0) {
console.warn("Maximum recursion depth exceeded in deepFreeze");
return obj;
}
try {
if (Array.isArray(obj)) {
obj.forEach((element) => deepFreeze(element, depth - 1));
} else {
Object.keys(obj).forEach((key) => {
const prop = (obj as { [key: string]: unknown })[key];
// To prevent circular reference, check Object.isFrozen
if (!Object.isFrozen(prop)) {
deepFreeze(prop, depth - 1);
}
});
}
// Freeze self (no-op if already frozen)
return Object.freeze(obj) as Readonly<T>;
} catch (error) {
console.error("Error occurred in deepFreeze:", error);
return obj;
}
};
Discussion