From b18ddefe4208dd685864567d5b8d2ed81a782bb0 Mon Sep 17 00:00:00 2001 From: adjsky Date: Tue, 19 Mar 2024 22:16:58 +0300 Subject: [PATCH] infer combine return value as a tuple --- packages/resulto/src/result.ts | 14 +++++++++----- packages/resulto/src/utility.ts | 14 ++++++++++++-- packages/resulto/test/result.test-d.ts | 19 ++++++++++++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/packages/resulto/src/result.ts b/packages/resulto/src/result.ts index 09bdd54..1ff0757 100644 --- a/packages/resulto/src/result.ts +++ b/packages/resulto/src/result.ts @@ -3,6 +3,7 @@ import { type ErrFn, type ErrPredicate, type Fn, + Mutable, type Predicate, ResultError, type UnwrapErrs, @@ -805,10 +806,10 @@ export function fromThrowable( * will contain an array of `Ok` values, otherwise the returned `Result` will * contain the first `Err` error. */ -export function combine[]>( - results: T +export function combine[]>( + results: [...T] ): Result, UnwrapErrs[number]> { - const unwrapped = [] as UnwrapOks + const unwrapped = [] as Mutable> for (const result of results) { if (result.isErr()) { @@ -829,7 +830,10 @@ export function combine[]>( * returned `AsyncResult` will contain the first `Err` error. */ export function combineAsync< - T extends (Result | AsyncResult)[] ->(results: T): AsyncResult, UnwrapErrs[number]> { + T extends readonly ( + | Result + | AsyncResult + )[] +>(results: [...T]): AsyncResult, UnwrapErrs[number]> { return chain(Promise.all(results).then(combine)) } diff --git a/packages/resulto/src/utility.ts b/packages/resulto/src/utility.ts index 9e34ebd..04400c3 100644 --- a/packages/resulto/src/utility.ts +++ b/packages/resulto/src/utility.ts @@ -9,7 +9,10 @@ export type Fn = (value: T) => U export type ErrFn = (error: E) => F export type UnwrapOks< - T extends (Result | AsyncResult)[] + T extends readonly ( + | Result + | AsyncResult + )[] > = { [i in keyof T]: T[i] extends Result ? U @@ -19,7 +22,10 @@ export type UnwrapOks< } export type UnwrapErrs< - T extends (Result | AsyncResult)[] + T extends readonly ( + | Result + | AsyncResult + )[] > = { [i in keyof T]: T[i] extends Result ? U @@ -28,6 +34,10 @@ export type UnwrapErrs< : never } +export type Mutable = { + -readonly [P in keyof T]: T[P] +} + export class ResultError extends Error { data: unknown diff --git a/packages/resulto/test/result.test-d.ts b/packages/resulto/test/result.test-d.ts index cf75c0f..f147e8a 100644 --- a/packages/resulto/test/result.test-d.ts +++ b/packages/resulto/test/result.test-d.ts @@ -1,6 +1,7 @@ import { assertType, describe, test } from "vitest" -import { err, ok } from "../src" +import { combine, err, ok } from "../src" +import { combineAsync } from "../src/result" describe(".value, .error", () => { const value = 4 @@ -33,3 +34,19 @@ describe(".value, .error", () => { assertType(errResult.error) }) }) + +describe("combine/combineAsync", () => { + test("infers Ok values", () => { + { + const res1 = ok([4]) + const res2 = ok("") + + assertType<[number[], string]>(combine([res1, res2]).unwrap()) + assertType>( + combineAsync([res1, res2]).unwrap() + ) + } + + assertType<[number, string]>(combine([ok(4), ok("")]).unwrap()) + }) +})