Skip to content

Commit 0d7f883

Browse files
authored
Merge pull request #541 from phryneas/typesversions-compat
add 5.0.2 type definitions as fallback for TS<3.7
2 parents 5f86272 + f215409 commit 0d7f883

File tree

3 files changed

+321
-1
lines changed

3 files changed

+321
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@ typings/
5858
.env
5959

6060
.idea
61-
dist/
61+
/dist/
6262
website/build
6363
.rts2*

compat/pre-3.7/dist/index.d.ts

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

package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111
"react-native": "dist/immer.esm.js",
1212
"source": "src/immer.ts",
1313
"types": "./dist/immer.d.ts",
14+
"typesVersions": {
15+
">=3.7": {
16+
"*": [
17+
"./*"
18+
]
19+
},
20+
">=3.1": {
21+
"*": [
22+
"compat/pre-3.7/*"
23+
]
24+
}
25+
},
1426
"sideEffects": false,
1527
"scripts": {
1628
"test": "jest && yarn test:build && yarn test:flow",
@@ -53,6 +65,7 @@
5365
"homepage": "https://github.com/immerjs/immer#readme",
5466
"files": [
5567
"dist",
68+
"compat",
5669
"src"
5770
],
5871
"devDependencies": {

0 commit comments

Comments
 (0)