Skip to content

Commit

Permalink
feat(anzen): improve promise all typing (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
sviridoff authored Nov 28, 2023
1 parent 8b4496b commit 5522b52
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 34 deletions.
21 changes: 10 additions & 11 deletions packages/anzen/src/__tests__/anzen_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,35 +112,34 @@ describe('Result', () => {
it('when all promises are resolved with success, should return expected value', async () => {
const promise1 = Promise.resolve(Result.success(1));
const promise2 = Promise.resolve(Result.success(2));
const promise3 = Promise.resolve(Result.success('A'));
const result = await Result.promiseAll([
promise1,
promise2,
promise3,
]);
assert.equal(result.isSuccess, true);
assert.equal(result.isFailure, false);
assert.deepEqual(result.getValue(), [1, 2]);
});

it('when promises are resolved with failure, should return expected value', async () => {
const promise1 = Promise.resolve(Result.success(1));
async function promise1() {
const random = Math.random();
if (random < 0.5) {
return Result.success(1);
}
return Result.failure('a');
}
const promise2 = Promise.resolve(Result.failure(2));
const result = await Result.promiseAll([
promise1,
promise1(),
promise2,
]);
assert.equal(result.isSuccess, false);
assert.equal(result.isFailure, true);
assert.equal(result.getError(), 2);
});

it('when promise is rejected with success, should return expected value', async () => {
const err = new Error('err');
const promise = Promise.reject(err);
const result = await Result.promiseAll([promise]);
assert.equal(result.isSuccess, false);
assert.equal(result.isFailure, true);
assert.equal(result.getError(), err);
});
});

describe('fromThrowable', () => {
Expand Down
48 changes: 25 additions & 23 deletions packages/anzen/src/anzen.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
type ExtractFailure<T> = T extends ResultFailure<infer U> ? U : never;
type ExtractSuccess<T> = T extends ResultSuccess<infer U> ? U : never;
export type AwaitedResults<T extends readonly any[]> = Promise<
| ResultSuccess<{
[K in keyof T]: ExtractSuccess<Awaited<T[K]>> extends never
? never
: ExtractSuccess<Awaited<T[K]>>;
}>
| (ExtractFailure<Awaited<T[number]>> extends never
? never
: ResultFailure<ExtractFailure<Awaited<T[number]>>>)
>;

export type AnzenAnyResult<E, T> =
| AnzenResultFailure<E>
| AnzenResultSuccess<T>;
Expand Down Expand Up @@ -106,16 +119,14 @@ export class ResultFailure<E> {
}
}

async function handleResult(
whenResult: Promise<
AnzenResultFailure<any> | AnzenResultSuccess<any>
>,
async function handleResult<E, T>(
whenResult: Promise<AnzenAnyResult<E, T>>,
) {
const response = await whenResult;
if (response.isFailure) {
return Promise.reject(response);
const res = await whenResult;
if (res.isFailure) {
return Promise.reject(res.getError());
}
return response.getValue();
return res.getValue();
}

// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
Expand All @@ -128,26 +139,17 @@ export class Result {
return new ResultFailure<E>(err);
}

static async promiseAll(
whenResults: Promise<
AnzenResultFailure<any> | AnzenResultSuccess<any>
>[],
) {
static async promiseAll<T extends any[]>(
whenResults: [...T],
): AwaitedResults<T> {
const handledResults = whenResults.map(handleResult);
try {
const values = await Promise.all(handledResults);
// @ts-expect-error
return Result.success(values);
} catch (err: any) {
if (
!(
err instanceof ResultFailure ||
err instanceof ResultSuccess
)
) {
return Result.failure(err);
}
// We propagate result err.
return err;
// @ts-expect-error
return Result.failure(err);
}
}

Expand Down

0 comments on commit 5522b52

Please sign in to comment.