Skip to content

Commit

Permalink
Add variants (#9)
Browse files Browse the repository at this point in the history
* Fix missing test

* Add result variants

* Fix type errors with fn and asyncFn

* Add option variants
  • Loading branch information
bkiac authored Nov 3, 2023
1 parent c99350d commit 20a720b
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 40 deletions.
6 changes: 6 additions & 0 deletions src/helpers/fn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ describe.concurrent("asyncFn", () => {
const result = await wrappedFn()
expect(result.unwrap()).toEqual(42)
})

it("returns Err result when provided async function returns Err", async () => {
const wrappedFn = asyncFn(async () => Promise.resolve(Err("rekt")))
const result = await wrappedFn()
expect(result.unwrapErr()).toEqual("rekt")
})
})
13 changes: 8 additions & 5 deletions src/helpers/fn.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type {ResultValueType, ResultErrorType} from "../util"
import type {
ResultValueType,
ResultErrorType,
AsyncResultValueType,
AsyncResultErrorType,
} from "../util"
import type {Result} from "../result/interface"
import {PromiseResult} from "../result/promise"

Expand All @@ -9,9 +14,7 @@ export function fn<T extends (...args: any[]) => Result<any, any>>(
}

export function asyncFn<T extends (...args: any[]) => Promise<Result<any, any>>>(f: T) {
return function (
...args: Parameters<T>
): PromiseResult<ResultValueType<ReturnType<T>>, ResultErrorType<ReturnType<T>>> {
return new PromiseResult(f(...args))
return function (...args: Parameters<T>) {
return new PromiseResult<AsyncResultValueType<T>, AsyncResultErrorType<T>>(f(...args))
}
}
4 changes: 2 additions & 2 deletions src/helpers/try.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Panic} from "../error/panic"
import {type ErrorHandler, type ResultError, type StdError, toStdError} from "../error/result_error"
import {type ErrorHandler, type ResultError, StdError, toStdError} from "../error/result_error"
import {Err} from "../result/err"
import type {Result} from "../result/interface"
import {Ok} from "../result/ok"
Expand Down Expand Up @@ -35,7 +35,7 @@ export function tryFnWith<T, E extends ResultError>(
}

export function tryPromise<T>(promise: Promise<T>): PromiseResult<T, StdError> {
return new PromiseResult(
return new PromiseResult<T, StdError>(
promise.then(
(value) => Ok(value),
(error: unknown) => Err(toStdError(error)),
Expand Down
15 changes: 14 additions & 1 deletion src/option/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,17 @@ export interface OptionMethods<T> {
toJSON(): {meta: "Some"; data: T} | {meta: "None"}
}

export type Option<T> = Some<T> | None
export interface SomeVariant<T> {
readonly some: true
readonly none: false
readonly value: T
}

export interface NoneVariant {
readonly some: false
readonly none: true
}

export type OptionVariants<T> = SomeVariant<T> | NoneVariant

export type Option<T> = OptionVariants<T> & OptionMethods<T>
4 changes: 2 additions & 2 deletions src/option/none.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {Panic, UnwrapPanic} from "../error/panic"
import {inspectSymbol} from "../util"
import type {OptionMethods, Option} from "./interface"
import type {OptionMethods, Option, NoneVariant} from "./interface"
import type {Some} from "./some"

export class NoneImpl implements OptionMethods<never> {
export class NoneImpl implements NoneVariant, OptionMethods<never> {
readonly some = false
readonly none = true

Expand Down
2 changes: 1 addition & 1 deletion src/option/promise.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {describe, it, expect, vi} from "vitest"
import {Panic, UnwrapPanic, PromiseOption, Some, None} from ".."

function promiseSome<T>(value: T) {
return new PromiseOption(Promise.resolve(Some<T>(value)))
return new PromiseOption<T>(Promise.resolve(Some<T>(value)))
}

function promiseNone() {
Expand Down
4 changes: 2 additions & 2 deletions src/option/some.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type {Panic} from "../error/panic"
import {inspectSymbol} from "../util"
import type {OptionMethods, Option} from "./interface"
import type {OptionMethods, Option, SomeVariant} from "./interface"
import {None} from "./none"

export class SomeImpl<T> implements OptionMethods<T> {
export class SomeImpl<T> implements SomeVariant<T>, OptionMethods<T> {
readonly some = true
readonly none = false
readonly value: T
Expand Down
5 changes: 2 additions & 3 deletions src/result/err.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {Panic, UnwrapPanic} from "../error/panic"
import {inspectSymbol} from "../util"
import type {Result, ResultMethods} from "./interface"
import type {ErrVariant, Result, ResultMethods} from "./interface"
import type {Ok} from "./ok"

export class ErrImpl<E> implements ResultMethods<never, E> {
export class ErrImpl<E> implements ErrVariant<E>, ResultMethods<never, E> {
readonly ok = false
readonly value?: never
readonly err = true
readonly error: E

Expand Down
16 changes: 15 additions & 1 deletion src/result/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,18 @@ export interface ResultMethods<T, E> {
toJSON(): {meta: "Ok"; data: T} | {meta: "Err"; data: E}
}

export type Result<T, E> = Ok<T> | Err<E>
export interface OkVariant<T> {
readonly ok: true
readonly err: false
readonly value: T
}

export interface ErrVariant<E> {
readonly ok: false
readonly err: true
readonly error: E
}

export type ResultVariants<T, E> = OkVariant<T> | ErrVariant<E>

export type Result<T, E> = ResultVariants<T, E> & ResultMethods<T, E>
7 changes: 3 additions & 4 deletions src/result/ok.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import {Panic, UnwrapPanic} from "../error/panic"
import {inspectSymbol} from "../util"
import type {Err} from "./err"
import type {Result, ResultMethods} from "./interface"
import type {OkVariant, Result, ResultMethods} from "./interface"

export class OkImpl<T> implements ResultMethods<T, never> {
export class OkImpl<T> implements OkVariant<T>, ResultMethods<T, never> {
readonly ok = true
readonly value: T
readonly err = false
readonly error?: never
readonly value: T

constructor(value: T) {
this.value = value
Expand Down
30 changes: 11 additions & 19 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
import type {Option} from "./option/interface"
import type {PromiseOption} from "./option/promise"
import type {Result} from "./result/interface"
import type {PromiseResult} from "./result/promise"
import type {Err} from "./result/err"
import type {Ok} from "./result/ok"

export const inspectSymbol = Symbol.for("nodejs.util.inspect.custom")

export type ResultValueErrorType<T> = T extends
| Result<infer V, infer E>
| PromiseResult<infer V, infer E>
| Promise<Result<infer V, infer E>>
| ((...args: any[]) => Result<infer V, infer E>)
| ((...args: any[]) => PromiseResult<infer V, infer E>)
| ((...args: any[]) => Promise<Result<infer V, infer E>>)
export type ResultValueErrorType<T> = T extends (...args: any[]) => Ok<infer V> | Err<infer E>
? {value: V; error: E}
: never

export type ResultValueType<T> = ResultValueErrorType<T>["value"]
export type ResultErrorType<T> = ResultValueErrorType<T>["error"]

export type OptionValueType<T> = T extends
| Option<infer V>
| PromiseOption<infer V>
| Promise<Option<infer V>>
| ((...args: any[]) => Option<infer V>)
| ((...args: any[]) => PromiseOption<infer V>)
| ((...args: any[]) => Promise<Option<infer V>>)
? V
export type AsyncResultValueErrorType<T> = T extends (
...args: any[]
) => Promise<Ok<infer V> | Err<infer E>>
? {value: V; error: E}
: never

export type AsyncResultValueType<T> = AsyncResultValueErrorType<T>["value"]
export type AsyncResultErrorType<T> = AsyncResultValueErrorType<T>["error"]

0 comments on commit 20a720b

Please sign in to comment.