Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/result-async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
)
}

inspect(f: (t: T) => void | Promise<void>): ResultAsync<T, E> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isErr()) {
return new Err(res.error)
}

await f(res.value)
return new Ok(res.value)
}),
)
}

andThrough<F>(f: (t: T) => Result<unknown, F> | ResultAsync<unknown, F>): ResultAsync<T, E | F> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
Expand Down Expand Up @@ -158,6 +171,19 @@ export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
)
}

inspectErr(f: (e: E) => void | Promise<void>): ResultAsync<T, E> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isOk()) {
return new Ok(res.value)
}

await f(res.error)
return new Err(res.error)
}),
)
}

andThen<R extends Result<unknown, unknown>>(
f: (t: T) => R,
): ResultAsync<InferOkTypes<R>, InferErrTypes<R> | E>
Expand Down
38 changes: 38 additions & 0 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ interface IResult<T, E> {
*/
map<A>(f: (t: T) => A): Result<A, E>

/**
* Calls a function with the contained `Ok` value, leaving both `Ok` and `Err` untouched.
*
* This function can be used to perform side-effects without transforming the contained value.
*
* @param f a callback function that receives the contained `Ok` value
*/
inspect(f: (t: T) => void): Result<T, E>

/**
* Maps a `Result<T, E>` to `Result<T, F>` by applying a function to a
* contained `Err` value, leaving an `Ok` value untouched.
Expand All @@ -167,6 +176,15 @@ interface IResult<T, E> {
*/
mapErr<U>(f: (e: E) => U): Result<T, U>

/**
* Calls a function with the contained `Err` error, leaving both `Ok` and `Err` untouched.
*
* This function can be used to perform side-effects without transforming the contained error.
*
* @param f a callback function that receives the contained `Err` value
*/
inspectErr(f: (e: E) => void): Result<T, E>

/**
* Similar to `map` Except you must return a new `Result`.
*
Expand Down Expand Up @@ -324,11 +342,21 @@ export class Ok<T, E> implements IResult<T, E> {
return ok(f(this.value))
}

inspect(f: (t: T) => void): Result<T, E> {
f(this.value)
return ok(this.value)
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
mapErr<U>(_f: (e: E) => U): Result<T, U> {
return ok(this.value)
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
inspectErr(_f: (e: E) => void): Result<T, E> {
return ok(this.value)
}

andThen<R extends Result<unknown, unknown>>(
f: (t: T) => R,
): Result<InferOkTypes<R>, InferErrTypes<R> | E>
Expand Down Expand Up @@ -436,6 +464,16 @@ export class Err<T, E> implements IResult<T, E> {
return err(f(this.error))
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
inspect(_f: (t: T) => void): Result<T, E> {
return err(this.error)
}

inspectErr(f: (e: E) => void): Result<T, E> {
f(this.error)
return err(this.error)
}

andThrough<F>(_f: (t: T) => Result<unknown, F>): Result<T, E | F> {
return err(this.error)
}
Expand Down
172 changes: 167 additions & 5 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ describe('Result.Ok', () => {
expect(mapFn).toHaveBeenCalledTimes(1)
})

it('Inspects an Ok value', () => {
const okVal = ok(12)
const inspectorFn = vitest.fn((number) => {
console.log("got OK value", number)
})

const inspected = okVal.inspect(inspectorFn)

expect(inspected.isOk()).toBe(true)
expect(inspected._unsafeUnwrap()).toBe(12)
expect(inspectorFn).toHaveBeenCalledTimes(1)
})

it('Skips `mapErr`', () => {
const mapErrorFunc = vitest.fn((_error) => 'mapped error value')

Expand All @@ -67,6 +80,15 @@ describe('Result.Ok', () => {
expect(mapErrorFunc).not.toHaveBeenCalledTimes(1)
})

it('Skips `inspectErr`', () => {
const inspectFn = vitest.fn((_error) => 'mapped error value')

const notMapped = ok(12).inspectErr(inspectFn)

expect(notMapped.isOk()).toBe(true)
expect(inspectFn).not.toHaveBeenCalledTimes(1)
})

describe('andThen', () => {
it('Maps to an Ok', () => {
const okVal = ok(12)
Expand Down Expand Up @@ -137,7 +159,7 @@ describe('Result.Ok', () => {
describe('andTee', () => {
it('Calls the passed function but returns an original ok', () => {
const okVal = ok(12)
const passedFn = vitest.fn((_number) => {})
const passedFn = vitest.fn((_number) => { })

const teed = okVal.andTee(passedFn)

Expand All @@ -162,7 +184,7 @@ describe('Result.Ok', () => {
describe('orTee', () => {
it('Calls the passed function but returns an original err', () => {
const errVal = err(12)
const passedFn = vitest.fn((_number) => {})
const passedFn = vitest.fn((_number) => { })

const teed = errVal.orTee(passedFn)

Expand Down Expand Up @@ -332,6 +354,20 @@ describe('Result.Err', () => {
expect(hopefullyNotMapped._unsafeUnwrapErr()).toEqual(errVal._unsafeUnwrapErr())
})

it('Skips `inspect`', () => {
const errVal = err('I am your father')

const inspectorFn = vitest.fn((_value) => {
console.log("got value")
})

const notInspected = errVal.inspect(inspectorFn)

expect(notInspected.isErr()).toBe(true)
expect(inspectorFn).not.toHaveBeenCalled()
expect(notInspected._unsafeUnwrapErr()).toEqual(errVal._unsafeUnwrapErr())
})

it('Maps over an Err', () => {
const errVal = err('Round 1, Fight!')

Expand All @@ -344,6 +380,20 @@ describe('Result.Err', () => {
expect(mapped._unsafeUnwrapErr()).not.toEqual(errVal._unsafeUnwrapErr())
})

it('Inspects an Err', () => {
const errVal = err('Round 1, Fight!')

const inspectorFn = vitest.fn((error: string) => {
console.error("Error happened", error)
})

const inspected = errVal.inspectErr(inspectorFn)

expect(inspected.isErr()).toBe(true)
expect(inspectorFn).toHaveBeenCalledTimes(1)
expect(inspected._unsafeUnwrapErr()).toEqual(errVal._unsafeUnwrapErr())
})

it('unwrapOr and return the default value', () => {
const okVal = err<number, string>('Oh nooo')
expect(okVal.unwrapOr(1)).toEqual(1)
Expand Down Expand Up @@ -376,7 +426,7 @@ describe('Result.Err', () => {
it('Skips over andTee', () => {
const errVal = err('Yolo')

const mapper = vitest.fn((_val) => {})
const mapper = vitest.fn((_val) => { })

const hopefullyNotFlattened = errVal.andTee(mapper)

Expand Down Expand Up @@ -877,6 +927,118 @@ describe('ResultAsync', () => {
})
})

describe("inspect", () => {
it('Inspects a value using a synchronous function', async () => {
const asyncVal = okAsync(12)

const inspectorFn = vitest.fn((number) => {
console.log("got value", number)
})

const inspected = asyncVal.inspect(inspectorFn)

expect(inspected).toBeInstanceOf(ResultAsync)

const newVal = await inspected

expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe(12)
expect(inspectorFn).toHaveBeenCalledTimes(1)
})

it('Inspects a value using a asynchronous function', async () => {
const asyncVal = okAsync(12)

const inspectorFn = vitest.fn(async (number) => {
console.log("got value", number)
})

const inspected = asyncVal.inspect(inspectorFn)

expect(inspected).toBeInstanceOf(ResultAsync)

const newVal = await inspected

expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe(12)
expect(inspectorFn).toHaveBeenCalledTimes(1)
})

it('Skips an error', async () => {
const asyncErr = errAsync<number, string>('Wrong format')

const inspectorFn = vitest.fn((number) => {
console.log("got value", number)
})

const notInspected = asyncErr.inspect(inspectorFn)

expect(notInspected).toBeInstanceOf(ResultAsync)

const newVal = await notInspected

expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Wrong format')
expect(inspectorFn).toHaveBeenCalledTimes(0)
})
})

describe("inspectErr", () => {
it('Inspects an error using an synchronous function', async () => {
const asyncErr = errAsync('Wrong format')

const inspectorFn = vitest.fn((str) => {
console.error("error happened", str)
})

const inspectedErr = asyncErr.inspectErr(inspectorFn)

expect(inspectedErr).toBeInstanceOf(ResultAsync)

const newVal = await inspectedErr

expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Wrong format')
expect(inspectorFn).toHaveBeenCalledTimes(1)
})

it('Inspects an error using an asynchronous function', async () => {
const asyncErr = errAsync('Wrong format')

const inspectorFn = vitest.fn(async (str) => {
console.error("error happened", str)
})

const inspectedErr = asyncErr.inspectErr(inspectorFn)

expect(inspectedErr).toBeInstanceOf(ResultAsync)

const newVal = await inspectedErr

expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Wrong format')
expect(inspectorFn).toHaveBeenCalledTimes(1)
})

it('Skips a value', async () => {
const asyncVal = okAsync(12)

const inspectorFn = vitest.fn((str) => {
console.error("got error", str)
})

const notInspected = asyncVal.inspectErr(inspectorFn)

expect(notInspected).toBeInstanceOf(ResultAsync)

const newVal = await notInspected

expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe(12)
expect(inspectorFn).toHaveBeenCalledTimes(0)
})
})

describe('mapErr', () => {
it('Maps an error using a synchronous function', async () => {
const asyncErr = errAsync('Wrong format')
Expand Down Expand Up @@ -1067,7 +1229,7 @@ describe('ResultAsync', () => {
describe('andTee', () => {
it('Calls the passed function but returns an original ok', async () => {
const okVal = okAsync(12)
const passedFn = vitest.fn((_number) => {})
const passedFn = vitest.fn((_number) => { })

const teed = await okVal.andTee(passedFn)

Expand All @@ -1092,7 +1254,7 @@ describe('ResultAsync', () => {
describe('orTee', () => {
it('Calls the passed function but returns an original err', async () => {
const errVal = errAsync(12)
const passedFn = vitest.fn((_number) => {})
const passedFn = vitest.fn((_number) => { })

const teed = await errVal.orTee(passedFn)

Expand Down