@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
106 lines (105 loc) • 3.25 kB
JavaScript
import { _stringify } from '../string/stringify.js';
import { _assertErrorClassOrRethrow } from './assert.js';
import { UnexpectedPassError } from './error.util.js';
/**
* Calls a function, returns a Tuple of [error, value].
* Allows to write shorter code that avoids `try/catch`.
* Useful e.g. in unit tests.
*
* Similar to pTry, but for sync functions.
*
* ERR is typed as Error, not `unknown`. While unknown would be more correct,
* according to recent TypeScript, Error gives more developer convenience.
* In our code we NEVER throw non-errors.
* Only possibility of non-error is in the 3rd-party library code, in these cases it
* can be manually cast to `unknown` for extra safety.
*
* @example
*
* const [err, v] = _try(() => someFunction())
* if (err) ...do something...
* v // go ahead and use v
*/
export function _try(fn, errorClass) {
try {
return [null, fn()];
}
catch (err) {
if (errorClass) {
_assertErrorClassOrRethrow(err, errorClass);
}
return [err, null];
}
}
/**
* Like _try, but for Promises.
*/
export async function pTry(promise, errorClass) {
try {
return [null, await promise];
}
catch (err) {
if (errorClass) {
_assertErrorClassOrRethrow(err, errorClass);
}
return [err, null];
}
}
/**
* Calls `fn`, expects is to throw, catches the expected error and returns.
* If error was NOT thrown - throws UnexpectedPassError instead.
*
* If `errorClass` is passed:
* 1. It automatically infers it's type
* 2. It does `instanceof` check and throws if wrong Error instance was thrown.
*/
export function _expectedError(fn, errorClass) {
try {
fn();
}
catch (err) {
if (errorClass && !(err instanceof errorClass)) {
console.warn(`_expectedError expected ${errorClass.constructor.name} but got different error class`);
throw err;
}
return err; // this is expected!
}
// Unexpected!
throw new UnexpectedPassError();
}
/**
* Awaits passed `promise`, expects is to throw (reject), catches the expected error and returns.
* If error was NOT thrown - throws UnexpectedPassError instead.
*
* If `errorClass` is passed:
* 1. It automatically infers it's type
* 2. It does `instanceof` check and throws if wrong Error instance was thrown.
*/
export async function pExpectedError(promise, errorClass) {
try {
await promise;
}
catch (err) {
if (errorClass && !(err instanceof errorClass)) {
console.warn(`pExpectedError expected ${errorClass.constructor.name} but got different error class`);
throw err;
}
return err; // this is expected!
}
// Unexpected!
throw new UnexpectedPassError();
}
/**
* Shortcut function to simplify error snapshot-matching in tests.
*/
export async function pExpectedErrorString(promise, errorClass) {
const err = await pExpectedError(promise, errorClass);
return _stringify(err);
}
/**
* Shortcut function to simplify error snapshot-matching in tests.
*/
export function _expectedErrorString(fn, errorClass) {
const err = _expectedError(fn, errorClass);
return _stringify(err);
}