Enrich a caught Error and rethrow in angular/typescript

Viewed 1146

I am new to frontend development - I used to be backend only for last years. I am trying to remove a error-silencer in a helper and rethrow the swallowed error with more informations. In the custom ErrorHandler I will catch the error end send the information to a backend service. I do not know how to do this in the correct way. The throwing part (simplified):

class Helper {
  public doStuff(input: string): string {
    try {
      return mightThrowAnException(input);
    } catch (err) {
      (err as any).inputData = input; // this destroys the error data
      throw err;
    }
  }
}

And catch this with


export class GlobalErrorHandler extends ErrorHandler {

  constructor(
    private injector: Injector,
  ) {
    super();
  }

  public handleError(error: Error) {
    let loggingData: any = {
      errorMessage: error.message,
    }

    if (typeof (error as any).inputData !== "undefined") { // would like to replace this duck-typing, too
      loggingData = {
        ...loggingData,
        inputData: (error as any).inputData,
      }
    }

    // ... more stuff here

    console.log(loggingData); // will send the data to the logging server ITRL
  }
}

To enrich the error I already tried to use @jsdevtools/ono package:

class HelperWithOno {
  public doStuff(input: string): string {
    try {
      return mightThrowAnException(input);
    } catch (err) {
      throw ono(err, {inputData: input});
    }
  }
}

Which did the job. Unfortunately the TS linter does not like the usage of symbol ( https://github.com/JS-DevTools/ono/issues/13 ) so I get a red build-pipeline.

In PHP with Zend I would create a class which extends the general Exception in the namespace of the helper and add the inputData property. Then I would be able to check the type of the caught exception and handle it in the desired way.

But I am pretty new to angular and TypeScript and not sure how to perform this task.

  • I could extend the Error class but
    • would I need to copy each property from the "previous" error manually? Or should I introduce another property previousError to store the original error?
    • where (directory) store the class-file?
    • should I create an injectable factory for the errors?
    • in which module would a experienced developer assume this class?
  • Wait till ono is fixed
  • Any other best practices?
1 Answers

There was no answer so I just post my solution; hope someone will tell me if it is wrong/buggy and propose a better solution.

I created a folder with the name errors in the the directory of the class Helper. In this directory is a file with the class

export class ProcessError extends Error {
    public inputData: string;
    public originalError: Error;

    public constructor(error: Error, inputData: string) {
        const errorAsAny = error as any;

        if ('fileName' in errorAsAny) {
            // Firefox specific
            if ('lineNumber' in errorAsAny) {
                // @ts-ignore
                super(error.message, errorAsAny.filename, errorAsAny.lineNumber);
            } else {
                // @ts-ignore
                super(error.message, errorAsAny.filename);
            }
        } else {
            super(error.message);
        }

        // Let's apply some magic you can find at https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
        Object.setPrototypeOf(this, ProcessError.prototype);

        this.originalError = error;
        this.inputData = inputData;
        this.stack = error.stack.split('\n').join('\n'); // materialize stack; not sure whether required
    }
}

Now I am able to throw the exception with

class HelperWithCustomError {
  public doStuff(input: string): string {
    try {
      return mightThrowAnException(input);
    } catch (err) {
      throw new ProcessError(err, input);
    }
  }
}

and check for the instance instead of duck-typing with

if (error instnaceof ProcessError)
Related