| 1 | type Tail<T extends any[]> = ((...t: T) => any) extends (
|
|---|
| 2 | _: any,
|
|---|
| 3 | ...tail: infer TT
|
|---|
| 4 | ) => any
|
|---|
| 5 | ? TT
|
|---|
| 6 | : []
|
|---|
| 7 |
|
|---|
| 8 | type PrimitiveType = number | string | boolean
|
|---|
| 9 |
|
|---|
| 10 | /** Object types that should never be mapped */
|
|---|
| 11 | type AtomicObject =
|
|---|
| 12 | | Function
|
|---|
| 13 | | WeakMap<any, any>
|
|---|
| 14 | | WeakSet<any>
|
|---|
| 15 | | Promise<any>
|
|---|
| 16 | | Date
|
|---|
| 17 | | RegExp
|
|---|
| 18 |
|
|---|
| 19 | export type Draft<T> = T extends PrimitiveType
|
|---|
| 20 | ? T
|
|---|
| 21 | : T extends AtomicObject
|
|---|
| 22 | ? T
|
|---|
| 23 | : T extends Map<infer K, infer V>
|
|---|
| 24 | ? DraftMap<K, V>
|
|---|
| 25 | : T extends Set<infer V>
|
|---|
| 26 | ? DraftSet<V>
|
|---|
| 27 | : T extends object
|
|---|
| 28 | ? {-readonly [K in keyof T]: Draft<T[K]>}
|
|---|
| 29 | : T
|
|---|
| 30 |
|
|---|
| 31 | // Inline these in ts 3.7
|
|---|
| 32 | interface DraftMap<K, V> extends Map<Draft<K>, Draft<V>> {}
|
|---|
| 33 |
|
|---|
| 34 | // Inline these in ts 3.7
|
|---|
| 35 | interface DraftSet<V> extends Set<Draft<V>> {}
|
|---|
| 36 |
|
|---|
| 37 | /** Convert a mutable type into a readonly type */
|
|---|
| 38 | export type Immutable<T> = T extends PrimitiveType
|
|---|
| 39 | ? T
|
|---|
| 40 | : T extends AtomicObject
|
|---|
| 41 | ? T
|
|---|
| 42 | : T extends Map<infer K, infer V> // Ideally, but wait for TS 3.7: ? Omit<ImmutableMap<K, V>, "set" | "delete" | "clear">
|
|---|
| 43 | ? ImmutableMap<K, V>
|
|---|
| 44 | : T extends Set<infer V> // Ideally, but wait for TS 3.7: ? Omit<ImmutableSet<V>, "add" | "delete" | "clear">
|
|---|
| 45 | ? ImmutableSet<V>
|
|---|
| 46 | : T extends object
|
|---|
| 47 | ? {readonly [K in keyof T]: Immutable<T[K]>}
|
|---|
| 48 | : T
|
|---|
| 49 |
|
|---|
| 50 | interface ImmutableMap<K, V> extends Map<Immutable<K>, Immutable<V>> {}
|
|---|
| 51 |
|
|---|
| 52 | interface ImmutableSet<V> extends Set<Immutable<V>> {}
|
|---|
| 53 |
|
|---|
| 54 | export interface Patch {
|
|---|
| 55 | op: "replace" | "remove" | "add"
|
|---|
| 56 | path: (string | number)[]
|
|---|
| 57 | value?: any
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | export type PatchListener = (patches: Patch[], inversePatches: Patch[]) => void
|
|---|
| 61 |
|
|---|
| 62 | /** Converts `nothing` into `undefined` */
|
|---|
| 63 | type FromNothing<T> = T extends Nothing ? undefined : T
|
|---|
| 64 |
|
|---|
| 65 | /** The inferred return type of `produce` */
|
|---|
| 66 | export type Produced<Base, Return> = Return extends void
|
|---|
| 67 | ? Base
|
|---|
| 68 | : Return extends Promise<infer Result>
|
|---|
| 69 | ? Promise<Result extends void ? Base : FromNothing<Result>>
|
|---|
| 70 | : FromNothing<Return>
|
|---|
| 71 |
|
|---|
| 72 | /**
|
|---|
| 73 | * The `produce` function takes a value and a "recipe function" (whose
|
|---|
| 74 | * return value often depends on the base state). The recipe function is
|
|---|
| 75 | * free to mutate its first argument however it wants. All mutations are
|
|---|
| 76 | * only ever applied to a __copy__ of the base state.
|
|---|
| 77 | *
|
|---|
| 78 | * Pass only a function to create a "curried producer" which relieves you
|
|---|
| 79 | * from passing the recipe function every time.
|
|---|
| 80 | *
|
|---|
| 81 | * Only plain objects and arrays are made mutable. All other objects are
|
|---|
| 82 | * considered uncopyable.
|
|---|
| 83 | *
|
|---|
| 84 | * Note: This function is __bound__ to its `Immer` instance.
|
|---|
| 85 | *
|
|---|
| 86 | * @param {any} base - the initial state
|
|---|
| 87 | * @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
|
|---|
| 88 | * @param {Function} patchListener - optional function that will be called with all the patches produced here
|
|---|
| 89 | * @returns {any} a new state, or the initial state if nothing was modified
|
|---|
| 90 | */
|
|---|
| 91 | export interface IProduce {
|
|---|
| 92 | /** Curried producer */
|
|---|
| 93 | <
|
|---|
| 94 | Recipe extends (...args: any[]) => any,
|
|---|
| 95 | Params extends any[] = Parameters<Recipe>,
|
|---|
| 96 | T = Params[0]
|
|---|
| 97 | >(
|
|---|
| 98 | recipe: Recipe
|
|---|
| 99 | ): <Base extends Immutable<T>>(
|
|---|
| 100 | base: Base,
|
|---|
| 101 | ...rest: Tail<Params>
|
|---|
| 102 | ) => Produced<Base, ReturnType<Recipe>>
|
|---|
| 103 | // ^ by making the returned type generic, the actual type of the passed in object is preferred
|
|---|
| 104 | // over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
|
|---|
| 105 | // Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
|
|---|
| 106 |
|
|---|
| 107 | /** Curried producer with initial state */
|
|---|
| 108 | <
|
|---|
| 109 | Recipe extends (...args: any[]) => any,
|
|---|
| 110 | Params extends any[] = Parameters<Recipe>,
|
|---|
| 111 | T = Params[0]
|
|---|
| 112 | >(
|
|---|
| 113 | recipe: Recipe,
|
|---|
| 114 | initialState: Immutable<T>
|
|---|
| 115 | ): <Base extends Immutable<T>>(
|
|---|
| 116 | base?: Base,
|
|---|
| 117 | ...rest: Tail<Params>
|
|---|
| 118 | ) => Produced<Base, ReturnType<Recipe>>
|
|---|
| 119 |
|
|---|
| 120 | /** Normal producer */
|
|---|
| 121 | <Base, D = Draft<Base>, Return = void>(
|
|---|
| 122 | base: Base,
|
|---|
| 123 | recipe: (draft: D) => Return,
|
|---|
| 124 | listener?: PatchListener
|
|---|
| 125 | ): Produced<Base, Return>
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | export const produce: IProduce
|
|---|
| 129 | export default produce
|
|---|
| 130 |
|
|---|
| 131 | /**
|
|---|
| 132 | * Like `produce`, but instead of just returning the new state,
|
|---|
| 133 | * a tuple is returned with [nextState, patches, inversePatches]
|
|---|
| 134 | *
|
|---|
| 135 | * Like produce, this function supports currying
|
|---|
| 136 | */
|
|---|
| 137 | export interface IProduceWithPatches {
|
|---|
| 138 | /** Curried producer */
|
|---|
| 139 | <
|
|---|
| 140 | Recipe extends (...args: any[]) => any,
|
|---|
| 141 | Params extends any[] = Parameters<Recipe>,
|
|---|
| 142 | T = Params[0]
|
|---|
| 143 | >(
|
|---|
| 144 | recipe: Recipe
|
|---|
| 145 | ): <Base extends Immutable<T>>(
|
|---|
| 146 | base: Base,
|
|---|
| 147 | ...rest: Tail<Params>
|
|---|
| 148 | ) => [Produced<Base, ReturnType<Recipe>>, Patch[], Patch[]]
|
|---|
| 149 | // ^ by making the returned type generic, the actual type of the passed in object is preferred
|
|---|
| 150 | // over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
|
|---|
| 151 | // Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
|
|---|
| 152 |
|
|---|
| 153 | /** Curried producer with initial state */
|
|---|
| 154 | <
|
|---|
| 155 | Recipe extends (...args: any[]) => any,
|
|---|
| 156 | Params extends any[] = Parameters<Recipe>,
|
|---|
| 157 | T = Params[0]
|
|---|
| 158 | >(
|
|---|
| 159 | recipe: Recipe,
|
|---|
| 160 | initialState: Immutable<T>
|
|---|
| 161 | ): <Base extends Immutable<T>>(
|
|---|
| 162 | base?: Base,
|
|---|
| 163 | ...rest: Tail<Params>
|
|---|
| 164 | ) => [Produced<Base, ReturnType<Recipe>>, Patch[], Patch[]]
|
|---|
| 165 |
|
|---|
| 166 | /** Normal producer */
|
|---|
| 167 | <Base, D = Draft<Base>, Return = void>(
|
|---|
| 168 | base: Base,
|
|---|
| 169 | recipe: (draft: D) => Return
|
|---|
| 170 | ): [Produced<Base, Return>, Patch[], Patch[]]
|
|---|
| 171 | }
|
|---|
| 172 | export const produceWithPatches: IProduceWithPatches
|
|---|
| 173 |
|
|---|
| 174 | /** Use a class type for `nothing` so its type is unique */
|
|---|
| 175 | declare class Nothing {
|
|---|
| 176 | // This lets us do `Exclude<T, Nothing>`
|
|---|
| 177 | private _: any
|
|---|
| 178 | }
|
|---|
| 179 |
|
|---|
| 180 | /**
|
|---|
| 181 | * The sentinel value returned by producers to replace the draft with undefined.
|
|---|
| 182 | */
|
|---|
| 183 | export const nothing: Nothing
|
|---|
| 184 |
|
|---|
| 185 | /**
|
|---|
| 186 | * To let Immer treat your class instances as plain immutable objects
|
|---|
| 187 | * (albeit with a custom prototype), you must define either an instance property
|
|---|
| 188 | * or a static property on each of your custom classes.
|
|---|
| 189 | *
|
|---|
| 190 | * Otherwise, your class instance will never be drafted, which means it won't be
|
|---|
| 191 | * safe to mutate in a produce callback.
|
|---|
| 192 | */
|
|---|
| 193 | export const immerable: unique symbol
|
|---|
| 194 |
|
|---|
| 195 | /**
|
|---|
| 196 | * Pass true to automatically freeze all copies created by Immer.
|
|---|
| 197 | *
|
|---|
| 198 | * By default, auto-freezing is disabled in production.
|
|---|
| 199 | */
|
|---|
| 200 | export function setAutoFreeze(autoFreeze: boolean): void
|
|---|
| 201 |
|
|---|
| 202 | /**
|
|---|
| 203 | * Pass true to use the ES2015 `Proxy` class when creating drafts, which is
|
|---|
| 204 | * always faster than using ES5 proxies.
|
|---|
| 205 | *
|
|---|
| 206 | * By default, feature detection is used, so calling this is rarely necessary.
|
|---|
| 207 | */
|
|---|
| 208 | export function setUseProxies(useProxies: boolean): void
|
|---|
| 209 |
|
|---|
| 210 | /**
|
|---|
| 211 | * Apply an array of Immer patches to the first argument.
|
|---|
| 212 | *
|
|---|
| 213 | * This function is a producer, which means copy-on-write is in effect.
|
|---|
| 214 | */
|
|---|
| 215 | export function applyPatches<S>(base: S, patches: Patch[]): S
|
|---|
| 216 |
|
|---|
| 217 | /**
|
|---|
| 218 | * Create an Immer draft from the given base state, which may be a draft itself.
|
|---|
| 219 | * The draft can be modified until you finalize it with the `finishDraft` function.
|
|---|
| 220 | */
|
|---|
| 221 | export function createDraft<T>(base: T): Draft<T>
|
|---|
| 222 |
|
|---|
| 223 | /**
|
|---|
| 224 | * Finalize an Immer draft from a `createDraft` call, returning the base state
|
|---|
| 225 | * (if no changes were made) or a modified copy. The draft must *not* be
|
|---|
| 226 | * mutated afterwards.
|
|---|
| 227 | *
|
|---|
| 228 | * Pass a function as the 2nd argument to generate Immer patches based on the
|
|---|
| 229 | * changes that were made.
|
|---|
| 230 | */
|
|---|
| 231 | export function finishDraft<T>(draft: T, listener?: PatchListener): Immutable<T>
|
|---|
| 232 |
|
|---|
| 233 | /** Get the underlying object that is represented by the given draft */
|
|---|
| 234 | export function original<T>(value: T): T | void
|
|---|
| 235 |
|
|---|
| 236 | /** Takes a snapshot of the current state of a draft and finalizes it (but without freezing). This is a great utility to print the current state during debugging (no Proxies in the way). The output of current can also be safely leaked outside the producer. */
|
|---|
| 237 | export function current<T>(value: T): T
|
|---|
| 238 |
|
|---|
| 239 | /** Returns true if the given value is an Immer draft */
|
|---|
| 240 | export function isDraft(value: any): boolean
|
|---|
| 241 |
|
|---|
| 242 | /** Returns true if the given value can be drafted by Immer */
|
|---|
| 243 | export function isDraftable(value: any): boolean
|
|---|
| 244 |
|
|---|
| 245 | export class Immer {
|
|---|
| 246 | constructor(config: {
|
|---|
| 247 | useProxies?: boolean
|
|---|
| 248 | autoFreeze?: boolean
|
|---|
| 249 | onAssign?: (
|
|---|
| 250 | state: ImmerState,
|
|---|
| 251 | prop: string | number,
|
|---|
| 252 | value: unknown
|
|---|
| 253 | ) => void
|
|---|
| 254 | onDelete?: (state: ImmerState, prop: string | number) => void
|
|---|
| 255 | onCopy?: (state: ImmerState) => void
|
|---|
| 256 | })
|
|---|
| 257 | /**
|
|---|
| 258 | * The `produce` function takes a value and a "recipe function" (whose
|
|---|
| 259 | * return value often depends on the base state). The recipe function is
|
|---|
| 260 | * free to mutate its first argument however it wants. All mutations are
|
|---|
| 261 | * only ever applied to a __copy__ of the base state.
|
|---|
| 262 | *
|
|---|
| 263 | * Pass only a function to create a "curried producer" which relieves you
|
|---|
| 264 | * from passing the recipe function every time.
|
|---|
| 265 | *
|
|---|
| 266 | * Only plain objects and arrays are made mutable. All other objects are
|
|---|
| 267 | * considered uncopyable.
|
|---|
| 268 | *
|
|---|
| 269 | * Note: This function is __bound__ to its `Immer` instance.
|
|---|
| 270 | *
|
|---|
| 271 | * @param {any} base - the initial state
|
|---|
| 272 | * @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
|
|---|
| 273 | * @param {Function} patchListener - optional function that will be called with all the patches produced here
|
|---|
| 274 | * @returns {any} a new state, or the initial state if nothing was modified
|
|---|
| 275 | */
|
|---|
| 276 | produce: IProduce
|
|---|
| 277 | /**
|
|---|
| 278 | * When true, `produce` will freeze the copies it creates.
|
|---|
| 279 | */
|
|---|
| 280 | readonly autoFreeze: boolean
|
|---|
| 281 | /**
|
|---|
| 282 | * When true, drafts are ES2015 proxies.
|
|---|
| 283 | */
|
|---|
| 284 | readonly useProxies: boolean
|
|---|
| 285 | /**
|
|---|
| 286 | * Pass true to automatically freeze all copies created by Immer.
|
|---|
| 287 | *
|
|---|
| 288 | * By default, auto-freezing is disabled in production.
|
|---|
| 289 | */
|
|---|
| 290 | setAutoFreeze(autoFreeze: boolean): void
|
|---|
| 291 | /**
|
|---|
| 292 | * Pass true to use the ES2015 `Proxy` class when creating drafts, which is
|
|---|
| 293 | * always faster than using ES5 proxies.
|
|---|
| 294 | *
|
|---|
| 295 | * By default, feature detection is used, so calling this is rarely necessary.
|
|---|
| 296 | */
|
|---|
| 297 | setUseProxies(useProxies: boolean): void
|
|---|
| 298 | }
|
|---|
| 299 |
|
|---|
| 300 | export interface ImmerState<T = any> {
|
|---|
| 301 | parent?: ImmerState
|
|---|
| 302 | base: T
|
|---|
| 303 | copy: T
|
|---|
| 304 | assigned: {[prop: string]: boolean; [index: number]: boolean}
|
|---|
| 305 | }
|
|---|
| 306 |
|
|---|
| 307 | // Backward compatibility with --target es5
|
|---|
| 308 | declare global {
|
|---|
| 309 | interface Set<T> {}
|
|---|
| 310 | interface Map<K, V> {}
|
|---|
| 311 | interface WeakSet<T> {}
|
|---|
| 312 | interface WeakMap<K extends object, V> {}
|
|---|
| 313 | }
|
|---|
| 314 |
|
|---|
| 315 | export declare function enableAllPlugins(): void
|
|---|
| 316 | export declare function enableES5(): void
|
|---|
| 317 | export declare function enableMapSet(): void
|
|---|
| 318 | export declare function enablePatches(): void
|
|---|