Skip to content

Latest commit

 

History

History
263 lines (225 loc) · 9.54 KB

File metadata and controls

263 lines (225 loc) · 9.54 KB

Error

The @perfective/common/error package helps organize exceptions and error handling. It defines an Exception, based on the JS Error class, that supports localizable error messages and error chaining; and provides functions to handle error stack output.

Exception

Exception type definition.
class Exception extends Error {
    public readonly name: string = 'Exception';
    public message: string; // (1)

    public readonly template: string; // (2)
    public readonly tokens: ExceptionTokens; // (3)
    public readonly context: ExceptionContext; // (4)
    public readonly previous: Error | null; // (5)

    public toString(): string; // (6)
}
  1. Inherited from Error, message is generated from the template and tokens.

  2. Message template with tokens wrapped in {{ }}, e.g. User ID {{id}} or User {{user_id}}.

  3. Token/Value map to be used in the error message.

  4. Additional context information. Context is not rendered by default, but may be useful in applications.

  5. Provide a previous error, if exists.

  6. Human-readable output of all the errors.

Constructors

  • exception(message: string, tokens: ExceptionTokens = {}, context: ExceptionContext = {}): Exception — creates an Exception.

  • chained(message: string, tokens: ExceptionTokens = {}, context: ExceptionContext = {}): (previous: Error) ⇒ Exception — creates a function to wrap a previous Error into an Exception.

  • causedBy(previous: Error, message: string, tokens: ExceptionTokens = {}, context: ExceptionContext = {}): Exception — creates an Exception with a previous Error.

  • caughtError(value: unknown): Error | Exception — wraps a non-Error value into an Exception. The Exception.message starts with Caught and contains the caught value coerced to a string.

    If given an Error, returns it as is.

Custom exceptions

  • invalidArgumentException(argument: string, expected: string, actual: string): Exception — creates an exception with the message about an invalid argument.

Usage

import { causedBy, chainStack, exception, rethrows, throws } from '@perfective/common/error';
import { decimal } from '@perfective/common/number';

function positive(input: number): string {
    if (input <= 0) {
        return throws(exception('Invalid input {{input}}', { // (1)
            input: decimal(input);
        }));
    }
    return decimal(10);
}

function field(name: string, value: number): string {
    try {
        return `${name} = ${positive(value)}`;
    }
    catch (error) {
        return throws(causedBy(error, 'Failed to output field {{field}}', { // (2)
            field: name,
        }));
    }
}

function record(): Record<string, string> {
    try {
        return {
            username: field('username', -42),
        }
    }
    catch (error) {
        return rethrows(error, 'Failed to build a record'); // (3)
    }
}

try {
    record();
}
catch (error) {
    console.error(error.toString()); // (4)
    // Outputs:
    //  Exception: Failed to build a record
    //      - Exception: Failed to output field `username`
    //      - Exception: Invalid input `-42`

    console.error(chainStack(error)); // (5)
    // Outputs:
    //  Exception: Failed to build a record
    //      at Object.rethrows (...)
    //      at Object.record (...)
    //      ...
    //  Caused by: Exception: Failed to output field `username`
    //      at Object.causedBy (...)
    //      at Object.throws (...)
    //      at Object.field (...)
    //      ...
    //  Caused by: Exception: Invalid input `-42`
    //      at Object.exception (...)
    //      at Object.throws (...)
    //      at Object.positive (...)
    //      ...
}
  1. throws can be used with any Error type, but Exception supports passing context information separately, which can be convenient for localization or logging purposes.

  2. causedBy is a function to create an Exception with a present Exception.previous property.

  3. rethrows is a shortcut for the throws(causedBy()) call. It supports message context as well.

  4. error.toString() is explicitly re-defined on Exception to output all error messages.

  5. chainStack() allows to output all errors with their stack information (platform dependent).

Panic

type Panic = (cause?: unknown) ⇒ never — a function that throws an Error.

  • panic(message: string, tokens?: ExceptionTokens, context?: ExceptionContext): Panic — creates a function that throws an Exception with a given message template with tokens and additional context data. If the cause is defined, sets the cause as a previous error.

    Useful working with Promise.catch() and RxJS catchError().

  • panic<E extends Error>(error: Value<E>): Panic — creates a function that throws a given Error. Ignores the cause, even when if is defined.

    Use panic to create a callback for lazy evaluation.
    import { panic } from '@perfective/common/error';
    import { maybe } from '@perfective/common/maybe';
    
    export function example(input: string | null | undefined): string {
        return maybe(input)
            .or(panic('Input is not present')); // (1)
    }
    1. Must use panic(), as the fallback in Maybe.or() is called only when the input is not present. Using throws() will result in throwing an exception every time a function is called.

  • throws(message: string, tokens?: ExceptionTokens, context?: ExceptionContext): never — throws an Exception with a given message template with tokens and additional context data.

  • throws<E extends Error>(error: Value<E>): never — throws a given Error. If given a callback, throws an Error returned by the callback.

    Can be used to throw an exception from a one-line arrow function.

  • rethrows(previous: Error, message: string, tokens: ExceptionTokens = {}, context: ExceptionContext = {}): never — throws an Exception with a given message caused by a previous Error. Exception message may contain given tokens and additional context data.

    Similar to throws, but requires to provide a previous error.

Recovery

type Recovery<T> = (error: Error) ⇒ T — a function that handles a given Error and returns a recovery value.

Failure

The default JS Error class does not have toJSON method and is serialized as an empty object by JSON.stringify. This creates a problem for any attempt to transfer error information. Type Failure solved this problem by providing a record type to "serialize" Error and Exception. It omits stack information, but keeps the list of previous errors.

  • Failure

    • failure<E extends Error>(error: E): Failure — convert and Error or an Exception into a Failure record.

Standard built-in JS Error types

  • Error:

    • error(message: string): Error — instantiates a new Error.

    • isError<T>(value: Error | T): value is Error — returns true when the value is an instance of Error.

    • isNotError<T>(value: Error | T): value is T — returns true when the value is not an instance of Error.

  • EvalError:

    • evalError(message: string): EvalError — instantiates a new EvalError.

    • isEvalError<T>(value: EvalError | T): value is EvalError — returns true when the value is an instance of EvalError.

    • isNotEvalError<T>(value: EvalError | T): value is T — returns true when the value is not an instance of EvalError.

  • RangeError:

    • rangeError(message: string): RangeError — instantiates a new RangeError.

    • isRangeError<T>(value: RangeError | T): value is RangeError — returns true when the value is an instance of RangeError.

    • isNotRangeError<T>(value: RangeError | T): value is T — returns true when the value is not an instance of RangeError.

  • ReferenceError:

    • referenceError(message: string): ReferenceError — instantiates a new ReferenceError.

    • isReferenceError<T>(value: ReferenceError | T): value is ReferenceError — returns true when the value is an instance of ReferenceError.

    • isNotReferenceError<T>(value: ReferenceError | T): value is T — returns true when the value is not an instance of ReferenceError.

  • SyntaxError:

    • syntaxError(message: string): SyntaxError — instantiates a new SyntaxError.

    • isSyntaxError<T>(value: SyntaxError | T): value is SyntaxError — returns true when the value is an instance of SyntaxError.

    • isNotSyntaxError<T>(value: SyntaxError | T): value is T — returns true when the value is not an instance of SyntaxError.

  • TypeError:

    • typeError(message: string): TypeError — instantiates a new TypeError.

    • isTypeError<T>(value: TypeError | T): value is TypeError — returns true when the value is an instance of TypeError.

    • isNotTypeError<T>(value: TypeError | T): value is T — returns true when the value is not an instance of TypeError.

Note
  • InternalError is non-standard and won’t be supported.

  • URIError will be supported in the @perfective/common/url package.

Roadmap