Skip to content

Commit

Permalink
feat: OrElse callback should be able to change Ok type
Browse files Browse the repository at this point in the history
  • Loading branch information
inaiat committed Apr 12, 2024
1 parent 3eebd1d commit d3c9909
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 22 deletions.
2 changes: 1 addition & 1 deletion BUILD_SHA
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c2279236936f1c27556aa707d729f7cf991781b4
3eebd1d3875783ff1f972f8bfac9d810a18d479e
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@inaiat/resultar",
"version": "0.8.8",
"version": "0.9.0",
"exports": "./src/index.ts"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "resultar",
"version": "0.8.8",
"version": "0.9.0",
"description": "Result pattern for typescript",
"type": "module",
"packageManager": "[email protected]",
Expand Down
20 changes: 10 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export * from './result.js'
export * from './result-async.js'
export {
Result, ok, err, fromThrowable, safeTry,
} from './result.js'
export {
ResultAsync, okAsync, errAsync, fromPromise, fromSafePromise, safeTryAsync, fromThrowableAsync,
} from './result-async.js'
10 changes: 7 additions & 3 deletions src/result-async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,13 @@ export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
)
}

orElse<R extends Result<T, unknown>>(f: (e: E) => R): ResultAsync<T, InferErrTypes<R>>
orElse<R extends ResultAsync<T, unknown>>(f: (e: E) => R): ResultAsync<T, InferAsyncErrTypes<R>>
orElse<A>(f: (e: E) => Result<T, A> | ResultAsync<T, A>): ResultAsync<T, A>
orElse<R extends Result<unknown, unknown>>(
f: (e: E) => R,
): ResultAsync<InferOkTypes<R> | T, InferErrTypes<R>>
orElse<R extends ResultAsync<unknown, unknown>>(
f: (e: E) => R,
): ResultAsync<InferAsyncOkTypes<R> | T, InferAsyncErrTypes<R>>
orElse<U, A>(f: (e: E) => Result<U, A> | ResultAsync<U, A>): ResultAsync<U | T, A>
orElse(f: any): any {
return new ResultAsync(
this.innerPromise.then(async (res: Result<T, E>) => { // eslint-disable-line @typescript-eslint/no-unsafe-argument
Expand Down
8 changes: 5 additions & 3 deletions src/result.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
CombineResults, CombineResultsWithAllErrorsArray, combineResultList, combineResultListWithAllErrors,
CombineResults, CombineResultsWithAllErrorsArray, InferErrTypes, InferOkTypes, combineResultList, combineResultListWithAllErrors,
} from './utils.js'
import { ErrorConfig, createResultarError } from './error.js'
import { ResultAsync, errAsync } from './result-async.js'
Expand Down Expand Up @@ -225,8 +225,10 @@ export class Result<T, E> implements Resultable<T, E> {
* @param f A function to apply to an `Err` value, leaving `Ok` values
* untouched.
*/
orElse<Y>(f: (t: E) => Result<T, Y>): Result<T, Y>
orElse<Y>(f: (t: E) => Result<T, Y>): Result<T, E | Y> {
orElse<R extends Result<unknown, unknown>>(
f: (e: E) => R,
): Result<InferOkTypes<R> | T, InferErrTypes<R>>
orElse<U, A>(f: (e: E) => Result<U, A>): Result<U | T, A> {
if (this.state.ok) {
return ok(this.value as Exclude<T, Promise<any>>)
}
Expand Down
42 changes: 41 additions & 1 deletion tests/result-test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
import { describe, it, mock } from 'node:test'
import assert, {
Expand Down Expand Up @@ -973,6 +972,47 @@ await describe('ResultAsync', async () => {
})
})

await describe('OrElse', async () => {
const foo = () => okAsync('foo')
const bar = () => errAsync(new Error('bar'))

class FooError extends Error {}
class BarError extends Error {}
class XooError extends Error {}

await it('Infer result', async () => {
type R = Result<string | number, never>
const result: R = await foo().orElse(() => okAsync(1))
equal(result._unsafeUnwrap(), 'foo')
})

await it('Set explicit type', async () => {
type R = Result<string | boolean, never>
const result: R = await foo().orElse<string | boolean, never>(() => okAsync(true))
equal(result._unsafeUnwrap(), 'foo')
})

await it('Infer multiples types', async () => {
type R = Result<string | undefined, string | Error>
const result: R = await bar().orElse(e => {
if (e instanceof BarError) {
return okAsync(undefined)
}

if (e instanceof FooError) {
return okAsync('foo')
}

if (e instanceof XooError) {
return errAsync('xoo')
}

return errAsync(e)
})
deepStrictEqual(result._unsafeUnwrapErr(), new Error('bar'))
})
})

await describe('Utils', async () => {
await describe('`Result.combine`', async () => {
await describe('Synchronous `combine`', async () => {
Expand Down

0 comments on commit d3c9909

Please sign in to comment.