Skip to content

Commit

Permalink
Merge branch 'readonly_types' of https://github.com/Mossop/immer into…
Browse files Browse the repository at this point in the history
… improved-types
  • Loading branch information
mweststrate committed Jan 14, 2020
2 parents 71f67e5 + cdc50a9 commit 4204af5
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 22 deletions.
14 changes: 14 additions & 0 deletions __tests__/draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ test("draft.ts", () => {
assert(fromDraft(toDraft(weak)), weak)
}

// ReadonlyMap instance
{
let val: ReadonlyMap<any, any> = _
let draft: Map<any, any> = _
assert(toDraft(val), draft)
}

// Set instance
{
let val: Set<any> = _
Expand All @@ -199,6 +206,13 @@ test("draft.ts", () => {
assert(fromDraft(toDraft(weak)), weak)
}

// ReadonlySet instance
{
let val: ReadonlySet<any> = _
let draft: Set<any> = _
assert(toDraft(val), draft)
}

// Promise object
{
let val: Promise<any> = _
Expand Down
36 changes: 36 additions & 0 deletions __tests__/immutable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,41 @@ test("types are ok", () => {
assert(val, _ as {readonly a: {readonly b: string}})
}

// Map
{
let val = _ as Immutable<Map<string, string>>
assert(val, _ as ReadonlyMap<string, string>)
}

// Already immutable Map
{
let val = _ as Immutable<ReadonlyMap<string, string>>
assert(val, _ as ReadonlyMap<string, string>)
}

// object in Map
{
let val = _ as Immutable<Map<{a: string}, {b: string}>>
assert(val, _ as ReadonlyMap<{readonly a: string}, {readonly b: string}>)
}

// Set
{
let val = _ as Immutable<Set<string>>
assert(val, _ as ReadonlySet<string>)
}

// Already immutable Set
{
let val = _ as Immutable<ReadonlySet<string>>
assert(val, _ as ReadonlySet<string>)
}

// object in Set
{
let val = _ as Immutable<Set<{a: string}>>
assert(val, _ as ReadonlySet<{readonly a: string}>)
}

expect(true).toBe(true)
})
20 changes: 18 additions & 2 deletions __tests__/produce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,28 @@ it("works with readonly Map and Set", () => {
const s = new Set<S>([{x: 2}])

const res1 = produce(m, (draft: Draft<Map<string, S>>) => {
assert(draft, _ as Map<string, {readonly x: number}>) // TODO: drop readonly in TS 3.7
assert(draft, _ as Map<string, {x: number}>)
})
assert(res1, _ as Map<string, {readonly x: number}>)

const res2 = produce(s, (draft: Draft<Set<S>>) => {
assert(draft, _ as Set<{readonly x: number}>) // TODO: drop readonly in TS 3.7
assert(draft, _ as Set<{x: number}>)
})
assert(res2, _ as Set<{readonly x: number}>)
})

it("works with ReadonlyMap and ReadonlySet", () => {
type S = {readonly x: number}
const m: ReadonlyMap<string, S> = new Map([["a", {x: 1}]])
const s: ReadonlySet<S> = new Set([{x: 2}])

const res1 = produce(m, (draft: Draft<Map<string, S>>) => {
assert(draft, _ as Map<string, {x: number}>)
})
assert(res1, _ as ReadonlyMap<string, {readonly x: number}>)

const res2 = produce(s, (draft: Draft<Set<S>>) => {
assert(draft, _ as Set<{x: number}>)
})
assert(res2, _ as ReadonlySet<{readonly x: number}>)
})
38 changes: 18 additions & 20 deletions src/types-external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,44 @@ type Tail<T extends any[]> = ((...t: T) => any) extends (
/** Object types that should never be mapped */
type AtomicObject =
| Function
| WeakMap<any, any>
| WeakSet<any>
| Promise<any>
| Date
| RegExp
| Boolean
| Number
| String

/**
* These should also never be mapped but must be tested after regular Map and
* Set
*/
type WeakReferences = WeakMap<any, any> | WeakSet<any>

export type Draft<T> = T extends AtomicObject
? T
: T extends Map<infer K, infer V>
? DraftMap<K, V>
: T extends Set<infer V>
? DraftSet<V>
: T extends ReadonlyMap<infer K, infer V> // Map extends ReadonlyMap
? Map<Draft<K>, Draft<V>>
: T extends ReadonlySet<infer V> // Set extends ReadonlySet
? Set<Draft<V>>
: T extends WeakReferences
? T
: T extends object
? {-readonly [K in keyof T]: Draft<T[K]>}
: T

// Inline these in ts 3.7
interface DraftMap<K, V> extends Map<Draft<K>, Draft<V>> {}

// Inline these in ts 3.7
interface DraftSet<V> extends Set<Draft<V>> {}

/** Convert a mutable type into a readonly type */
export type Immutable<T> = T extends AtomicObject
? T
: T extends Map<infer K, infer V> // Ideally, but wait for TS 3.7: ? Omit<ImmutableMap<K, V>, "set" | "delete" | "clear">
? ImmutableMap<K, V>
: T extends Set<infer V> // Ideally, but wait for TS 3.7: ? Omit<ImmutableSet<V>, "add" | "delete" | "clear">
? ImmutableSet<V>
: T extends ReadonlyMap<infer K, infer V> // Map extends ReadonlyMap
? ReadonlyMap<Immutable<K>, Immutable<V>>
: T extends ReadonlySet<infer V> // Set extends ReadonlySet
? ReadonlySet<Immutable<V>>
: T extends WeakReferences
? T
: T extends object
? {readonly [K in keyof T]: Immutable<T[K]>}
: T

interface ImmutableMap<K, V> extends Map<Immutable<K>, Immutable<V>> {}

interface ImmutableSet<V> extends Set<Immutable<V>> {}

export interface Patch {
op: "replace" | "remove" | "add"
path: (string | number)[]
Expand Down

0 comments on commit 4204af5

Please sign in to comment.