Skip to content

Commit

Permalink
improve Draft<T> in TypeScript
Browse files Browse the repository at this point in the history
- make Draft<T> work in TS 2.8.x
- make Draft<T> do nothing for tuples, dates, RegExp objects, functions,
  and boxed primitives

Fixes #215
  • Loading branch information
aleclarson committed Oct 28, 2018
1 parent 18a25b8 commit 512256b
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 5 deletions.
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ const newState = produce<State>(state, draft => {

This ensures that the only place you can modify your state is in your produce callbacks. It even works recursively and with `ReadonlyArray`s!

Using TypeScript v2.8.x and lower is _not_ supported by Immer.

## Immer on older JavaScript environments?

By default `produce` tries to use proxies for optimal performance. However, on older JavaScript engines `Proxy` is not available. For example, when running Microsoft Internet Explorer or React Native on Android. In such cases, Immer will fallback to an ES5 compatible implementation which works identical, but is a bit slower.
Expand Down
24 changes: 19 additions & 5 deletions src/immer.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// Mapped type to remove readonly modifiers from state
// Based on https://github.com/Microsoft/TypeScript/blob/d4dc67aab233f5a8834dff16531baf99b16fea78/tests/cases/conformance/types/conditional/conditionalTypes1.ts#L120-L129
export type DraftObject<T> = {-readonly [P in keyof T]: Draft<T[P]>}
export interface DraftArray<T> extends Array<Draft<T>> {}
/** Object types that should never be mapped */
type AtomicObject = Function | Date | RegExp | Boolean | Number | String

/** Use type inference to know when an array is finite */
type IsFinite<T extends any[]> = T extends never[]
? true
: T extends ReadonlyArray<infer U> ? (U[] extends T ? false : true) : true

export type DraftObject<T> = T extends object
? T extends AtomicObject ? T : {-readonly [P in keyof T]: Draft<T[P]>}
: T

export type DraftArray<T> = Array<
T extends ReadonlyArray<any>
? {[P in keyof T]: Draft<T>}[keyof T]
: DraftObject<T>
>

export type Draft<T> = T extends any[]
? DraftArray<T[number]>
? IsFinite<T> extends true ? T : DraftArray<T[number]>
: T extends ReadonlyArray<any>
? DraftArray<T[number]>
: T extends object ? DraftObject<T> : T
Expand Down

0 comments on commit 512256b

Please sign in to comment.