Skip to content

Commit

Permalink
Fix fn, asyncFn types (#11)
Browse files Browse the repository at this point in the history
* Fix asyncFn types

* Fix fn test
  • Loading branch information
bkiac authored Nov 7, 2023
1 parent 110c85f commit a178a7e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 11 deletions.
96 changes: 94 additions & 2 deletions src/helpers/fn.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import {describe, expect, it} from "vitest"
import {asyncFn, fn, Ok, Err} from "../internal"
import {describe, expect, it, expectTypeOf} from "vitest"
import {
asyncFn,
fn,
Ok,
Err,
tryAsyncFn,
type PromiseResult,
type StdError,
type Result,
tryFn,
} from "../internal"

describe.concurrent("fn", () => {
it("returns Ok result when provided function does not throw", () => {
Expand All @@ -13,6 +23,41 @@ describe.concurrent("fn", () => {
const result = wrappedFn()
expect(result.unwrapErr()).toEqual("rekt")
})

describe("types", () => {
it("returns correct type with function returning Ok | Err", () => {
const f = (_arg: number) => {
if (Math.random() > 0.5) {
return Ok(1)
}
return Err("error")
}
const wrapped = fn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<Result<number, string>>()
})

it("returns correct type with function returning Ok", () => {
const f = (_arg: number) => Ok(1)
const wrapped = fn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<Result<number, unknown>>()
})

it("returns correct type with function returning Err", () => {
const f = (_arg: number) => Err(1)
const wrapped = fn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<Result<unknown, number>>()
})

it("returns correct type with function returning Result", () => {
const f = (_arg: number) => tryFn(() => 1)
const wrapped = fn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<Result<number, StdError>>()
})
})
})

describe.concurrent("asyncFn", () => {
Expand All @@ -27,4 +72,51 @@ describe.concurrent("asyncFn", () => {
const result = await wrappedFn()
expect(result.unwrapErr()).toEqual("rekt")
})

describe("types", () => {
it("returns correct type with function returning Promise<Ok | Err>", () => {
const f = async (_arg: number) => {
if (Math.random() > 0.5) {
return Ok(1)
}
return Err("error")
}
const wrapped = asyncFn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<PromiseResult<number, string>>()
})

it("returns correct type with function returning Promise<Ok>", () => {
const f = async (_arg: number) => Ok(1)
const wrapped = asyncFn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<PromiseResult<number, unknown>>()
})

it("returns correct type with function returning Promise<Err>", () => {
const f = async (_arg: number) => Err(1)
const wrapped = asyncFn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<PromiseResult<unknown, number>>()
})

it("returns correct type with function returning PromiseResult", () => {
const f = (_arg: number) => tryAsyncFn(async () => 1)
const wrapped = asyncFn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<PromiseResult<number, StdError>>()
})

it("returns correct type with function returning Promise<Result>", () => {
const f = async (_arg: number) => {
const bar = tryAsyncFn(async () => {
return 1
})
return bar
}
const wrapped = asyncFn(f)
expectTypeOf(wrapped).parameter(0).toBeNumber()
expectTypeOf(wrapped).returns.toEqualTypeOf<PromiseResult<number, StdError>>()
})
})
})
6 changes: 5 additions & 1 deletion src/helpers/fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export function fn<T extends (...args: any[]) => Result<any, any>>(
return f
}

export function asyncFn<T extends (...args: any[]) => Promise<Result<any, any>>>(f: T) {
export function asyncFn<
T extends
| ((...args: any[]) => PromiseResult<any, any>)
| ((...args: any[]) => Promise<Result<any, any>>),
>(f: T) {
return function (...args: Parameters<T>) {
return new PromiseResult<AsyncResultValueType<T>, AsyncResultErrorType<T>>(f(...args))
}
Expand Down
6 changes: 4 additions & 2 deletions src/result/promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import type {Result} from "./interface"
import type {Panic} from "../error/panic"

export class PromiseResult<T, E> implements PromiseLike<Result<T, E>> {
constructor(readonly promise: Promise<Result<T, E>> | PromiseLike<Result<T, E>>) {}
constructor(
readonly promise: Promise<Result<T, E>> | PromiseLike<Result<T, E>> | PromiseResult<T, E>,
) {}

then<A, B>(
successCallback?: (res: Result<T, E>) => A | PromiseLike<A>,
Expand All @@ -12,7 +14,7 @@ export class PromiseResult<T, E> implements PromiseLike<Result<T, E>> {
}

catch<B>(rejectionCallback?: (reason: unknown) => B | PromiseLike<B>): PromiseLike<B> {
return this.promise.then(null, rejectionCallback)
return this.promise.then(undefined, rejectionCallback)
}

finally(callback: () => void): PromiseLike<Result<T, E>> {
Expand Down
20 changes: 14 additions & 6 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import type {PromiseResult} from "./result/promise"
import type {Err} from "./result/err"
import type {Ok} from "./result/ok"
import type {Result} from "./result/interface"

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

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

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

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

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

0 comments on commit a178a7e

Please sign in to comment.