Use fork.do or patch.do to batch operations efficiently.
import { fork, patch } from'patchfork'const nextState = fork.do({ user: { name: 'Nick Cave', age: 50 } }, (state) => {
// `state` is a shallow clone of the input object,// i.e. it's a regular JS object, not a Proxy.console.log(state) // { user: { name: 'Nick Cave', age: 50 } }// Call `patch(state)` to make changes.patch(state).user.name('Nicholas Cage')
patch(state).user.age(51)
// TypeScript will prevent you from doing unsafe mutation.// ❌ Error: Property 'age' is readonly
state.user.age = 52// ❌ Error: state.user not patchablepatch(state.user).name('Nicholas Cage')
})
Optional chaining
fork and patch always do optional chaining. It works just like JavaScript optional chaining. i.e. the whole expression will evaluate to undefined if the operation can't be performed.
import { fork } from'patchfork'interfaceUser {
name?: string
settings?: Map<string, string>
}
constuser: User = {}
// 'assignment' operations on optional properties always execute.fork(user).name('Joe')
// => { name: 'Joe' }// TS type: User// 'update' operations on optional properties will only execute if the property is not undefined.fork(user).name((name) => name.toUpperCase())
// => undefined// TS type: User | undefined// Collection methods will only succeed if the collection is not undefined or null.fork(user).settings.set('theme', 'dark')
// => undefined// TS type: User | undefined
You can use the ?? operator to perform a fallback operation if the first fails.
const y =
fork(user).name((name) => name.toUpperCase()) ?? fork(user).name('default')
// => { name: 'default' }// TS type: User
Updating values inside Maps
Maps allow keys of any type, so they need a separate syntax.
Use the special key symbol followed by the key itself in parentheses to operate on Map values.
TypeScript should prevent unsafely mutating data within patchfork's draft functions if your data is well-typed and you don't use as any ! But who knows what might happen later, in the outside world. Shit's crazy out there.
For extra peace of mind, call setDevMode(true) early in your application's boot process to freeze objects at development time.
This will cause errors to be thrown if you try to mutate an object that is supposed to be immutable.
👠It currently only with data supported by structuredClone (So yes ✅ to Map, Set, plain objects, and arrays. And no ❌ to custom classes, objects with symbol keys or getters/setters, etc)
It currently returns a new object even if an edit is ineffectual, e.g.
A clever (derogatory) immutable state utility for TypeScript.
The npm package patchfork receives a total of 2 weekly downloads. As such, patchfork popularity was classified as not popular.
We found that patchfork demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Package last updated on 26 Aug 2025
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
GitHub account BufferZoneCorp published sleeper packages that later added credential theft, GitHub Actions tampering, fake go wrappers, and SSH persistence.