You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中是否建议使用Object.freeze?还有哪些对象不可变实现方式?

Should You Use Object.freeze() for Immutability in TypeScript?

Great question! Let’s break this down. Object.freeze() is a valid tool for immutability, but it has caveats you need to be aware of:

  • It’s shallow by default: If your object has nested objects or arrays, those inner values can still be modified. For example:
    const frozenObj = Object.freeze({ nested: { count: 1 } });
    frozenObj.nested.count = 2; // This works at runtime (and TypeScript won’t flag it by default!)
    
  • TypeScript doesn’t automatically enforce read-only types: To get compile-time checks, pair it with the Readonly<T> utility type:
    type MyObj = { value: number };
    const frozen: Readonly<MyObj> = Object.freeze({ value: 10 });
    frozen.value = 20; // TypeScript throws an error here
    
  • Performance impact is minimal for simple objects: For most everyday use cases, the overhead of freezing is negligible. But for deeply nested or frequently created objects, it might add up.

So, to answer the core question: Object.freeze() is a good choice for flat, shallow objects where you want runtime immutability, but it’s not sufficient for nested structures without extra work.


Other Methods for Object Immutability in TypeScript

If Object.freeze() doesn’t meet your needs, here are the most common alternatives:

1. TypeScript’s Readonly<T> Utility Type

This is a compile-only safeguard—it doesn’t affect runtime behavior, but it stops you from accidentally modifying properties during development:

type User = { name: string; age: number };
const user: Readonly<User> = { name: "Miroslav", age: 35 };
user.age = 36; // TypeScript error: Cannot assign to 'age' because it is a read-only property
  • Pros: Lightweight, no runtime overhead, integrates seamlessly with TypeScript.
  • Cons: Doesn’t prevent runtime modifications (e.g., using type assertions or bypassing TypeScript).

2. Custom Deep Freeze Function

To fix Object.freeze()’s shallow limitation, you can write a recursive deep freeze function that freezes all nested objects:

function deepFreeze<T>(obj: T): Readonly<T> {
  // Freeze the top-level object
  Object.freeze(obj);

  // Recursively freeze nested objects/arrays
  Object.values(obj).forEach(value => {
    if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
      deepFreeze(value);
    }
  });

  return obj;
}

const deepFrozen = deepFreeze({ nested: { count: 1 } });
deepFrozen.nested.count = 2; // Throws an error in strict mode, and TypeScript flags it
  • Pros: Full runtime immutability for nested structures.
  • Cons: Higher performance cost for complex objects; frozen objects can’t be modified at all, which reduces flexibility.

3. Immutability Libraries

For production-grade immutability, especially with nested structures, dedicated libraries are a great option:

Immer

Immer lets you write "mutating" code that actually produces a new immutable object under the hood—super intuitive:

import { produce } from "immer";

const baseState = { user: { name: "Alice", age: 30 } };
const newState = produce(baseState, draft => {
  draft.user.age = 31; // Looks like we're modifying, but Immer creates a copy
});

console.log(baseState.user.age); // 30 (original remains unchanged)
console.log(newState.user.age); // 31 (new object with updated value)
  • Pros: Clean, familiar syntax; efficient structural sharing; handles nested structures effortlessly.
  • Cons: Adds a third-party dependency (though it’s lightweight).

Immutable.js

This library provides immutable data structures like Map and List that never modify the original instance—any change returns a new copy:

import { Map } from "immutable";

const user = Map({ name: "Bob", age: 25 });
const updatedUser = user.set("age", 26);

console.log(user.get("age")); // 25
console.log(updatedUser.get("age")); // 26
  • Pros: Battle-tested, full immutability, optimized for performance with large datasets.
  • Cons: Requires learning a new API; converting between native objects and Immutable.js structures adds extra steps.

4. Spread Operator & Object.assign (Shallow Immutability)

For simple shallow updates, use native JavaScript syntax to create a new object instead of modifying the original:

const original = { a: 1, b: 2 };
const updated = { ...original, b: 3 }; // Copy all properties, then override b

console.log(original.b); // 2
console.log(updated.b); // 3
  • Pros: No dependencies, uses native syntax, easy to implement for flat objects.
  • Cons: For nested structures, you have to recursively spread each level, which gets messy quickly.

Quick Recap
  • Use Object.freeze() + Readonly<T> for flat objects needing runtime + compile-time immutability.
  • Use Readonly<T> alone for compile-time safety without runtime restrictions.
  • Use a deep freeze function or Immer/Immutable.js for nested structures.
  • Use spread/Object.assign for simple shallow updates.

内容的提问来源于stack exchange,提问作者Miroslav Jonas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:03:20