@lodestar/utils
Version:
Utilities required across multiple lodestar packages
106 lines (85 loc) • 2.45 kB
text/typescript
const symErr = Symbol("err");
export type Err<T> = {[symErr]: true; error: T};
export type Result<T, E> = T | Err<E>;
export function Err<T>(error: T): Err<T> {
return {[symErr]: true, error};
}
/**
* Typeguard for Err<T>. Allows the pattern
* ```ts
* function getNumSquare(): Result<number, Error> {
* const value = getNum();
* if (isErr(value)) {
* return value; // return as error
* }
* return value ** 2;
* }
* function getNum(): Result<number, Error>
* ```
* Since the non-error is not wrapped, it uses a symbol to prevent collisions
*/
export function isErr<T, E>(result: Result<T, E>): result is Err<E> {
return result !== null && typeof result === "object" && (result as Err<E>)[symErr] === true;
}
/**
* Given an array of results, run a function only on an array of ok results.
* Returns a new array of results with same length as `results` where the ok
* value may be Err or T2.
*/
export function mapOkResults<T1, T2, E>(
results: Result<T1, E>[],
fn: (items: T1[]) => Result<T2, E>[]
): Result<T2, E>[] {
const oks: T1[] = [];
for (let i = 0; i < results.length; i++) {
const result = results[i];
if (!isErr(result)) {
oks.push(result);
}
}
const outOksResults = fn(oks);
if (outOksResults.length !== oks.length) {
throw Error("mapOkResults fn must return same length");
}
const outResults: Result<T2, E>[] = [];
for (let i = 0, j = 0; i < results.length; i++) {
const result = results[i];
if (isErr(result)) {
outResults.push(result);
} else {
outResults.push(outOksResults[j]);
j++;
}
}
return outResults;
}
/**
* See {@link mapOkResults} but `fn` is async
*/
export async function mapOkResultsAsync<T1, T2, E>(
results: Result<T1, E>[],
fn: (items: T1[]) => Promise<Result<T2, E>[]>
): Promise<Result<T2, E>[]> {
const oks: T1[] = [];
for (let i = 0; i < results.length; i++) {
const result = results[i];
if (!isErr(result)) {
oks.push(result);
}
}
const outOksResults = await fn(oks);
if (outOksResults.length !== oks.length) {
throw Error("mapOkResults fn must return same length");
}
const outResults: Result<T2, E>[] = [];
for (let i = 0, j = 0; i < results.length; i++) {
const result = results[i];
if (isErr(result)) {
outResults.push(result);
} else {
outResults.push(outOksResults[j]);
j++;
}
}
return outResults;
}