Skip to content

Commit 381bb16

Browse files
committed
feat: fork()
1 parent 1d4cc19 commit 381bb16

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

src/result-async.ts

+7
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
203203
return this._promise.then((res) => res.match(ok, _err))
204204
}
205205

206+
fork<A, B, C, D>(
207+
ok: (t: T) => Result<A, B> | ResultAsync<A, B>,
208+
err: (e: E) => Result<C, D> | ResultAsync<C, D>,
209+
): ResultAsync<A | C, B | D> {
210+
return new ResultAsync(this._promise.then(async (res) => res.match(ok, err)))
211+
}
212+
206213
unwrapOr<A>(t: A): Promise<T | A> {
207214
return this._promise.then((res) => res.unwrapOr(t))
208215
}

src/result.ts

+41
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,25 @@ interface IResult<T, E> {
275275
*/
276276
match<A, B = A>(ok: (t: T) => A, err: (e: E) => B): A | B
277277

278+
/**
279+
* Similar to `match` except the functions must return a new `Result`.
280+
*
281+
* @param ok
282+
* @param err
283+
*/
284+
fork<A, B, C, D>(ok: (t: T) => Result<A, B>, err: (e: E) => Result<C, D>): Result<A | C, B | D>
285+
286+
/**
287+
* Similar to `fork` except the functions must return a new `ResultAsync`.
288+
*
289+
* @param ok
290+
* @param err
291+
*/
292+
asyncFork<A, B, C, D>(
293+
ok: (t: T) => Result<A, B> | ResultAsync<A, B>,
294+
err: (e: E) => Result<C, D> | ResultAsync<C, D>,
295+
): ResultAsync<A | C, B | D>
296+
278297
/**
279298
* @deprecated will be removed in 9.0.0.
280299
*
@@ -394,6 +413,17 @@ export class Ok<T, E> implements IResult<T, E> {
394413
return ok(this.value)
395414
}
396415

416+
fork<A, B, C, D>(ok: (t: T) => Result<A, B>, _err: (e: E) => Result<C, D>): Result<A | C, B | D> {
417+
return ok(this.value)
418+
}
419+
420+
asyncFork<A, B, C, D>(
421+
ok: (t: T) => Result<A, B> | ResultAsync<A, B>,
422+
_err: (e: E) => Result<C, D> | ResultAsync<C, D>,
423+
): ResultAsync<A | C, B | D> {
424+
return new ResultAsync(Promise.resolve(ok(this.value)))
425+
}
426+
397427
safeUnwrap(): Generator<Err<never, E>, T> {
398428
const value = this.value
399429
/* eslint-disable-next-line require-yield */
@@ -493,6 +523,17 @@ export class Err<T, E> implements IResult<T, E> {
493523
return err(this.error)
494524
}
495525

526+
fork<A, B, C, D>(_ok: (t: T) => Result<A, B>, err: (e: E) => Result<C, D>): Result<A | C, B | D> {
527+
return err(this.error)
528+
}
529+
530+
asyncFork<A, B, C, D>(
531+
_ok: (t: T) => Result<A, B> | ResultAsync<A, B>,
532+
err: (e: E) => Result<C, D> | ResultAsync<C, D>,
533+
): ResultAsync<A | C, B | D> {
534+
return new ResultAsync(Promise.resolve(err(this.error)))
535+
}
536+
496537
safeUnwrap(): Generator<Err<never, E>, T> {
497538
const error = this.error
498539
return (function* () {

tests/index.test.ts

+52
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,19 @@ describe('Result.Ok', () => {
288288
expect(errMapper).not.toHaveBeenCalled()
289289
})
290290

291+
it('Forks on an Ok', () => {
292+
const okMapper = vitest.fn((_val) => ok('weeeeee'))
293+
const errMapper = vitest.fn((_val) => err('wooooo'))
294+
295+
const forked = ok(12).fork(okMapper, errMapper)
296+
297+
expect(forked.isOk()).toBe(true)
298+
expect(forked).toBeInstanceOf(Ok)
299+
expect(forked._unsafeUnwrap()).toBe('weeeeee')
300+
expect(okMapper).toHaveBeenCalledTimes(1)
301+
expect(errMapper).not.toHaveBeenCalled()
302+
})
303+
291304
it('Unwraps without issue', () => {
292305
const okVal = ok(12)
293306

@@ -450,6 +463,19 @@ describe('Result.Err', () => {
450463
expect(errMapper).toHaveBeenCalledTimes(1)
451464
})
452465

466+
it('Forks on an Err', () => {
467+
const okMapper = vitest.fn((_val) => ok('weeeeee'))
468+
const errMapper = vitest.fn((_val) => err('wooooo'))
469+
470+
const forked = err(12).fork(okMapper, errMapper)
471+
472+
expect(forked.isErr()).toBe(true)
473+
expect(forked).toBeInstanceOf(Err)
474+
expect(forked._unsafeUnwrapErr()).toBe('wooooo')
475+
expect(okMapper).not.toHaveBeenCalled()
476+
expect(errMapper).toHaveBeenCalledTimes(1)
477+
})
478+
453479
it('Throws when you unwrap an Err', () => {
454480
const errVal = err('woopsies')
455481

@@ -1171,6 +1197,32 @@ describe('ResultAsync', () => {
11711197
})
11721198
})
11731199

1200+
describe('fork', () => {
1201+
it('Forks on an Ok', async () => {
1202+
const okMapper = vitest.fn((_val) => ok('weeeeee'))
1203+
const errMapper = vitest.fn((_val) => err('wooooo'))
1204+
1205+
const forked = await okAsync(12).fork(okMapper, errMapper)
1206+
1207+
expect(forked.isOk()).toBe(true)
1208+
expect(forked._unsafeUnwrap()).toBe('weeeeee')
1209+
expect(okMapper).toHaveBeenCalledTimes(1)
1210+
expect(errMapper).not.toHaveBeenCalled()
1211+
})
1212+
1213+
it('Forks on an Error', async () => {
1214+
const okMapper = vitest.fn((_val) => ok('weeeeee'))
1215+
const errMapper = vitest.fn((_val) => err('wooooo'))
1216+
1217+
const forked = await errAsync('bad').fork(okMapper, errMapper)
1218+
1219+
expect(forked.isErr()).toBe(true)
1220+
expect(forked._unsafeUnwrapErr()).toBe('wooooo')
1221+
expect(okMapper).not.toHaveBeenCalled()
1222+
expect(errMapper).toHaveBeenCalledTimes(1)
1223+
})
1224+
})
1225+
11741226
describe('unwrapOr', () => {
11751227
it('returns a promise to the result value on an Ok', async () => {
11761228
const unwrapped = await okAsync(12).unwrapOr(10)

0 commit comments

Comments
 (0)