Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add non-spread overload for Result.all #125

Merged
merged 2 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/reference/api/result.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ to combine results with asynchronouse code.

// The actual signature is more complicated but this should be good enough.
static all(...results: Result<T, E>): Result<T[], E>
static all(results: Result<T, E>[]): Result<T[], E>

Parse a set of ``Result``, returning an array of all ``Ok`` values.
Short circuits with the first ``Err`` found, if any.
Expand All @@ -46,6 +47,18 @@ Example:

let [pizza, toppings] = result.unwrap(); // pizza is a Pizza, toppings is a Toppings. Could throw GetPizzaError or GetToppingsError.

When working with a set of results of unknown size, you can use the array version of ``all()``.

Example:

.. code-block:: typescript

let results: Result<Topping, GetToppingsError>[] = pizzaToppingNames.map(name => getPizzaToppingByName(name));

let result = Result.all(results); // Result<Topping[], GetToppingsError>

let toppings = result.unwrap(); // toppings is an array of Topping. Could throw GetToppingsError.

``andThen()``
-------------

Expand Down Expand Up @@ -87,6 +100,7 @@ Example:

// The actual signature is more complicated but this should be good enough.
static any(...results: Result<T, E>): Result<T, E[]>
static any(results: Result<T, E>[]): Result<T, E[]>

Parse a set of ``Result``, short-circuits when an input value is ``Ok``.
If no ``Ok`` is found, returns an ``Err`` containing the collected error values.
Expand All @@ -103,6 +117,18 @@ Example:

let url = result.unwrap(); // At least one attempt gave us a successful url

When working with a set of results of unknown size, you can use the array version of ``any()``.

Example:

.. code-block:: typescript

let connections: Array<Result<string, Error1 | Error2 | Error3 | ...>> = [attempt1(), attempt2(), attempt3(), ...];

let results = Result.any(connections); // Result<string, Error1 | Error2 | Error3 | ...>

let url = results.unwrap(); // At least one attempt gave us a successful url

``error``
---------

Expand Down
25 changes: 25 additions & 0 deletions src/result.ts
Original file line number Diff line number Diff line change
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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could likely be removed since the arrays I'm parsing above are typed as any[]. I could just as easily type the arguments as arg0: T[0] | T, argN: ...T but using these types are technically truer to what's going on.

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
Loading