Skip to content

Commit

Permalink
types: async produce
Browse files Browse the repository at this point in the history
  • Loading branch information
aleclarson committed Feb 2, 2019
1 parent 13b0ed0 commit c6cd632
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 14 deletions.
47 changes: 42 additions & 5 deletions __tests__/produce.ts
Expand Up @@ -196,15 +196,52 @@ it("does not enforce immutability at the type level", () => {
exactType(result, {} as any[])
})

it("can produce nothing", () => {
let result = produce({}, _ => nothing)
it("can produce an undefined value", () => {
let base = {} as {readonly a: number}

// Return only nothing.
let result = produce(base, _ => nothing)
exactType(result, undefined)

// Return maybe nothing.
let result2 = produce(base, draft => {
if (draft.a > 0) return nothing
})
exactType(result2, {} as typeof base | undefined)
})

it("can return the draft itself", () => {
let base = {} as {readonly a: number}
let result = produce(base, draft => draft)

// Currently, the `readonly` modifier is lost.
exactType(result, {} as {a: number} | undefined)
})

it("can return a promise", () => {
let base = {} as {readonly a: number}

// Return a promise only.
exactType(
produce(base, draft => {
return Promise.resolve(draft.a > 0 ? null : undefined)
}),
{} as Promise<{readonly a: number} | null>
)

// Return a promise or undefined.
exactType(
produce(base, draft => {
if (draft.a > 0) return Promise.resolve()
}),
{} as (Promise<{readonly a: number}> | {readonly a: number})
)
})

it("works with `void` hack", () => {
let obj = {} as {readonly a: number}
let res = produce(obj, s => void s.a++)
exactType(res, obj)
let base = {} as {readonly a: number}
let copy = produce(base, s => void s.a++)
exactType(copy, base)
})

it("works with generic parameters", () => {
Expand Down
16 changes: 7 additions & 9 deletions src/immer.d.ts
Expand Up @@ -43,17 +43,15 @@ export interface Patch {

export type PatchListener = (patches: Patch[], inversePatches: Patch[]) => void

type IsVoidLike<T> = T extends void | undefined ? 1 : 0

/** Converts `nothing` into `undefined` */
type FromNothing<T> = Nothing extends T ? Exclude<T, Nothing> | undefined : T
type FromNothing<T> = T extends Nothing ? undefined : T

/** The inferred return type of `produce` */
export type Produced<T, Return> = IsVoidLike<Return> extends 0
? FromNothing<Return>
: IsVoidLike<Return> extends 1
? T
: T | FromNothing<Exclude<Return, void>>
export type Produced<Base, Return> = Return extends void
? Base
: Return extends Promise<infer Result>
? Promise<Result extends void ? Base : FromNothing<Result>>
: FromNothing<Return>

type ImmutableTuple<T extends ReadonlyArray<any>> = {
readonly [P in keyof T]: Immutable<T[P]>
Expand Down Expand Up @@ -168,7 +166,7 @@ export function createDraft<T>(base: T): Draft<T>
* Pass a function as the 2nd argument to generate Immer patches based on the
* changes that were made.
*/
export function finishDraft<T>(base: Draft<T>, listener?: PatchListener): T
export function finishDraft<T>(draft: T, listener?: PatchListener): Immutable<T>

/** Get the underlying object that is represented by the given draft */
export function original<T>(value: T): T | void
Expand Down

0 comments on commit c6cd632

Please sign in to comment.