Skip to content

Commit

Permalink
Add non-spread overload for Result.all
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorSinnott committed Apr 17, 2024
1 parent cb3c471 commit 08f3d0b
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 25 deletions.
43 changes: 34 additions & 9 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ interface BaseResult<T, E> extends Iterable<T extends Iterable<infer U> ? U : ne
* @param msg the message to throw if no Err value.
*/
expectErr(msg: string): E;

/**
* Returns the contained `Ok` value.
* Because this function may throw, its use is generally discouraged.
Expand Down Expand Up @@ -125,27 +125,27 @@ interface BaseResult<T, E> extends Iterable<T extends Iterable<infer U> ? U : ne

/**
* Returns `Ok()` if we have a value, otherwise returns `other`.
*
*
* `other` is evaluated eagerly. If `other` is a result of a function
* call try `orElse()` instead – it evaluates the parameter lazily.
*
*
* @example
*
*
* Ok(1).or(Ok(2)) // => Ok(1)
* Err('error here').or(Ok(2)) // => Ok(2)
* Err('error here').or(Ok(2)) // => Ok(2)
*/
or<E2>(other: Result<T, E2>): Result<T, E2>

/**
* Returns `Ok()` if we have a value, otherwise returns the result
* of calling `other()`.
*
*
* `other()` is called *only* when needed and is passed the error value in a parameter.
*
*
* @example
*
*
* Ok(1).orElse(() => Ok(2)) // => Ok(1)
* Err('error').orElse(() => Ok(2)) // => Ok(2)
* Err('error').orElse(() => Ok(2)) // => Ok(2)
*/
orElse<E2>(other: (error: E) => Result<T, E2>): Result<T, E2>

Expand Down Expand Up @@ -437,9 +437,19 @@ export namespace Result {
* Parse a set of `Result`s, returning an array of all `Ok` values.
* Short circuits with the first `Err` found, if any
*/
export function all<const T extends Result<any, any>[]>(results: T): Result<ResultOkTypes<T>, ResultErrTypes<T>[number]>
export function all<T extends Result<any, any>[]>(
...results: T
): Result<ResultOkTypes<T>, ResultErrTypes<T>[number]>
export function all<T extends Result<any, any>[]>(
arg0: Head<T> | T, ...argN: Tail<T>
): Result<ResultOkTypes<T>, ResultErrTypes<T>[number]> {
const results = arg0 === undefined
? []
: Array.isArray(arg0)
? arg0 as T
: [arg0, ...argN] as T;

const okResult = [];
for (let result of results) {
if (result.isOk()) {
Expand All @@ -456,9 +466,19 @@ export namespace Result {
* Parse a set of `Result`s, short-circuits when an input value is `Ok`.
* If no `Ok` is found, returns an `Err` containing the collected error values
*/
export function any<const T extends Result<any, any>[]>(results: T): Result<ResultOkTypes<T>[number], ResultErrTypes<T>>
export function any<T extends Result<any, any>[]>(
...results: T
): Result<ResultOkTypes<T>[number], ResultErrTypes<T>>
export function any<T extends Result<any, any>[]>(
arg0: Head<T> | T, ...argN: Tail<T>
): Result<ResultOkTypes<T>[number], ResultErrTypes<T>> {
const results = arg0 === undefined
? []
: Array.isArray(arg0)
? arg0 as T
: [arg0, ...argN] as T;

const errResult = [];

// short-circuits
Expand Down Expand Up @@ -504,3 +524,8 @@ export namespace Result {
return val instanceof Err || val instanceof Ok;
}
}

// Utility types
type Head<T extends any[]> = T extends [any, ...infer R] ?
T extends [...infer F, ...R] ? F : never : never
type Tail<T extends any[]> = T extends [any, ...infer R] ? R : never
70 changes: 54 additions & 16 deletions test/result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,39 @@ test('Result.all', () => {
expect(all0).toMatchResult(Ok([]));
eq<typeof all0, Result<[], never>>(true);

const all0_array = Result.all([]);
expect(all0_array).toMatchResult(Ok([]));
eq<typeof all0_array, Result<[], never>>(true);

const all1 = Result.all(ok0, ok1);
expect(all1).toMatchResult(Ok([3, true]));
eq<typeof all1, Result<[number, boolean], never>>(true);

const all3 = Result.all(err0, err1);
expect(all3).toMatchResult(Err(err0.error));
eq<typeof all3, Result<[never, never], symbol | Error>>(true);
const all1Array = Result.all([ok0, ok1]);
expect(all1Array).toMatchResult(Ok([3, true]));
eq<typeof all1Array, Result<[number, boolean], never>>(true);

const all2 = Result.all(err0, err1);
expect(all2).toMatchResult(Err(err0.error));
eq<typeof all2, Result<[never, never], symbol | Error>>(true);

const all2Array = Result.all([err0, err1]);
expect(all2Array).toMatchResult(Err(err0.error));
eq<typeof all2Array, Result<[never, never], symbol | Error>>(true);

const all3 = Result.all(...([] as Result<string, number>[]));
eq<typeof all3, Result<string[], number>>(true);

const all3Array = Result.all([] as Result<string, number>[]);
eq<typeof all3Array, Result<string[], number>>(true);

const all4 = Result.all(...([] as Result<string, number>[]));
eq<typeof all4, Result<string[], number>>(true);
const all4 = Result.all(ok0, ok1, ok2, err2);
expect(all4).toMatchResult(Err(9));
eq<typeof all4, Result<[number, boolean, 8, boolean], boolean | 9>>(true);

const all5 = Result.all(ok0, ok1, ok2, err2);
expect(all5).toMatchResult(Err(9));
eq<typeof all5, Result<[number, boolean, 8, boolean], boolean | 9>>(true);
const all4Array = Result.all([ok0, ok1, ok2, err2]);
expect(all4Array).toMatchResult(Err(9));
eq<typeof all4Array, Result<[number, boolean, 8, boolean], boolean | 9>>(true);
});

test('Result.any', () => {
Expand All @@ -138,20 +157,39 @@ test('Result.any', () => {
expect(any0).toMatchResult(Err([]));
eq<typeof any0, Result<never, []>>(true);

const any0Array = Result.any();
expect(any0Array).toMatchResult(Err([]));
eq<typeof any0Array, Result<never, []>>(true);

const any1 = Result.any(ok0, ok1);
expect(any1).toMatchResult(Ok(3));
eq<typeof any1, Result<number | boolean, [never, never]>>(true);

const any3 = Result.any(err0, err1);
expect(any3).toMatchResult(Err([err0.error, err1.error]));
eq<typeof any3, Result<never, [symbol, Error]>>(true);
const any1Array = Result.any([ok0, ok1]);
expect(any1Array).toMatchResult(Ok(3));
eq<typeof any1Array, Result<number | boolean, [never, never]>>(true);

const any2 = Result.any(err0, err1);
expect(any2).toMatchResult(Err([err0.error, err1.error]));
eq<typeof any2, Result<never, [symbol, Error]>>(true);

const any2Array = Result.any([err0, err1]);
expect(any2Array).toMatchResult(Err([err0.error, err1.error]));
eq<typeof any2Array, Result<never, [symbol, Error]>>(true);

const any3 = Result.any(...([] as Result<string, number>[]));
eq<typeof any3, Result<string, number[]>>(true);

const any3Array = Result.any([] as Result<string, number>[]);
eq<typeof any3Array, Result<string, number[]>>(true);

const any4 = Result.any(...([] as Result<string, number>[]));
eq<typeof any4, Result<string, number[]>>(true);
const any4 = Result.any(err0, err1, err2, ok2);
expect(any4).toMatchResult(Ok(8));
eq<typeof any4, Result<boolean | 8, [symbol, Error, 9, boolean]>>(true);

const any5 = Result.any(err0, err1, err2, ok2);
expect(any5).toMatchResult(Ok(8));
eq<typeof any5, Result<boolean | 8, [symbol, Error, 9, boolean]>>(true);
const any4Array = Result.any([err0, err1, err2, ok2]);
expect(any4Array).toMatchResult(Ok(8));
eq<typeof any4Array, Result<boolean | 8, [symbol, Error, 9, boolean]>>(true);
});

test('Result.wrap', () => {
Expand Down

0 comments on commit 08f3d0b

Please sign in to comment.