用途
fp-immutable 是一份面向 TypeScript 开发者的实用不可变(immutability)模式参考,属于 fp-ts-skills 技能集。它通过大量代码示例,说明如何在不修改原始数据的前提下更新数组、对象和深层嵌套结构,并讨论何时直接修改(mutation)是合理甚至更优的选择。
主要能力
资料围绕以下主题展开:
- 不可变性的价值:共享状态导致意外行为、调试时保留历史状态、撤销/重做、React/Redux 中基于
===的变化检测、避免副作用。 - 展开运算符模式:数组的末尾添加、开头添加、按索引插入、按索引删除、按值删除、按索引更新、替换匹配项;对象的更新属性、添加属性、删除属性(解构 + 展开)、合并对象、条件更新。
- 嵌套数据更新:展示逐层展开的写法,并给出一个类似 lens 的
updateIn辅助函数,通过路径数组递归更新。 - 常见任务:切换列表项布尔值、按 ID 更新数组项、合并默认值、克隆并修改(含深拷贝数组与对象)。
- const 的真相:
const只阻止重新赋值,不阻止修改;readonly类型提供编译期不可变;Object.freeze()提供运行时浅冻结。 - readonly 类型:只读数组、只读对象、递归的
DeepReadonly类型。 - 何时可以修改:函数内部局部变量、构建局部结果、性能关键代码、一次性构建对象、局部数组填充。
- Immer:当展开嵌套过于痛苦时,使用
produce以“看似修改”的写法获得不可变结果;以及配合 React 的useImmer。 - 快速参考表:汇总常用任务的不可变写法。
使用方式
资料以代码片段形式呈现,可直接参考其中的模式编写 TypeScript 代码。示例包括:
- 数组操作:
[...items, 4]、[0, ...items]、[...items.slice(0, 1), 99, ...items.slice(1)]、items.filter(x => x !== 2)、items.map((x, i) => i === 1 ? 99 : x)。 - 对象操作:
{ ...user, age: 31 }、const { role, ...withoutRole } = user、{ ...defaults, ...prefs }、{ ...user, ...(shouldUpdate && { age: 31 }) }。 - 嵌套更新:逐层展开,或使用
updateIn(state, ['user', 'profile', 'settings', 'theme'], () => 'dark')。 - 常见任务函数:
toggleTodo、updateUser、createConfig、cloneOrder。 - 类型:
readonly T[]、Readonly<{...}>、DeepReadonly<T>。 - Immer:
import { produce } from 'immer',以及import { useImmer } from 'use-immer'。
适用场景
- 使用 React/Redux 等需要不可变状态更新的前端项目。
- 需要保留历史状态以支持调试、撤销/重做的应用。
- 希望避免共享状态副作用、提升代码可推理性的 TypeScript 代码库。
- 需要处理深层嵌套对象更新的场景,可选用 Immer 简化。
限制
- 资料为模式参考,未提供安装命令、包版本或完整项目配置。
Object.freeze()是浅冻结,嵌套对象仍可被修改。const不提供不可变性,仅阻止重新赋值。- 性能关键代码中,不可变写法可能带来 GC 压力,资料建议先做性能分析。
- 未提供关于 Immer 的安装方式、版本要求或与具体框架集成的完整步骤。