immer.d.ts 10 KB

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