JavaScript and TypeScript: Best practice for maintaining an Error's origin

Coming from a Java developer background, I am relatively new to JavaScript/TypeScript.

Is there a standard approach for handling and preserving the cause of an Error in JavaScript/TypeScript?

I aim to obtain a comprehensive stack trace when wrapping an Error within another, akin to how Java Exception stack traces work:

Message of exception 1
...
Caused by: Message of exception 2
...
Caused by: Message of the root exception

I attempted the following code, but err1 does not retain the reference to err2:

// CODE
try {
    try {
        throw new Error("Error no2");
    } catch (err2) {
        console.log("========================================");
        console.log(err2);
        throw new Error("Error no1");
    }
} catch (err1) {
    console.log("========================================");
    console.log(err1);
}
// CONSOLE OUTPUT
$ node test.ts 
========================================
Error: Error no2
    at Object.<anonymous> (/tmp/test.ts:3:15)
    at Module._compile (internal/modules/cjs/loader.js:956:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
    at Module.load (internal/modules/cjs/loader.js:812:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
    at internal/main/run_main_module.js:17:11
========================================
Error: Error no1
    at Object.<anonymous> (/tmp/test.ts:7:15)
    at Module._compile (internal/modules/cjs/loader.js:956:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
    at Module.load (internal/modules/cjs/loader.js:812:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
    at internal/main/run_main_module.js:17:11

Additionally, I have not found any property named cause in the Error class. While there is a stack property, modifying it may not be considered good practice.

Thank you!

Answer №1

New in 2021: There is a recent development in error management - the Error() constructor can now accept an options object as its second argument. This options object should include a cause property, which typically refers to another Error, although it is not mandatory.

For example

throw new Error('Outer Error', {cause: new Error('Inner Error')});

Compatibility

As of October 2021, this new constructor is supported by Firefox, Chrome, and Safari. To access the cause of an Error, you would need to examine its cause property. Additionally, Firefox provides additional information in the console, such as:

Uncaught Error: Outer Error
    <anonymous> debugger eval code:1
Caused by: Error: Inner Error
    <anonymous> debugger eval code:1

Currently, Edge does not recognize the second constructor property.

References

Mozilla documentation: https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error

ECMAScript proposal: https://github.com/tc39/proposal-error-cause

Answer №2

I have always relied on a modified version of Kristian's solution mentioned here

class TraceableError extends Error {
  trace: Error;

  constructor(message?: string, innerError?: Error) {
    super(message); 
    this.trace = innerError;

    const actualProto = new.target.prototype;

    if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); } 
    else { this.__proto__ = actualProto; } 
  }
}

To throw an error using this approach:

try {
    try {
        throw new Error("Error no2");
    } catch (err2) {
        console.log("========================================");
        console.log(err2);
        throw new TraceableError("Error no1", err2);
    }
} catch (err1) {
    console.log("========================================");
    console.log(err1);
}

Please note that if left unhandled, the error message in the trace section of the new error will not be displayed in the console.

Answer №3

One feature that I find very useful is the Error cause option. Here's a snippet of my text building function utilized by loggers:

export function getErrorMessageWithCausedByParts(err: Error, stack=true): string {
  const doNewLines = stack;
  let txt = "";
  
  if(stack) {
    txt+=err.stack;
  } else {
    txt+=err.message;
  }

  if(err.cause != null) {
    if (err.cause instanceof Error) {
      if(doNewLines) {
        txt+="\n[caused by] ";
      } else {
        txt += " [caused by] ";
      }
      
      txt += getErrorMessageWithCausedByParts(err.cause, stack);
    } else {
      txt += `[Unknown cause type?!]: ${JSON.stringify(err.cause)}`
    }
  }

  return txt;
}

Note: to access the cause option in the Error object, make sure to specify a newer ES version in your tsconfig.json file

"compilerOptions": {

    "lib": ["es2022"]

    ...
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it feasible to pre-load external websites using JavaScript?

While searching on various platforms, including Stack Overflow, I couldn't find a solution to this specific query. I'm not necessarily seeking an implementation already in place, but rather ... Imagine having an intranet application that loads q ...

Navigating websites: Clicking buttons and analyzing content

When looking to navigate a website's directory by utilizing buttons on the page or calling the associated functions directly, what language or environment is most suitable for this task? After experimenting with Python, Java, Selenium, and JavaScript, ...

Steps for removing routing in Angular 2 after setting the folder as a dependency for another project

In my testing Angular project (referred to as project1), I am developing components and utilizing routing for organizational and aesthetic purposes. There is another Angular project (referred to as project2) which includes the component project-project1 i ...

Display tables based on selected changes with dynamically changing options

My HTML structure displays rows with active and inactive statuses based on a select change, and it's currently functioning correctly. However, the project requirements have changed, allowing for more status options besides just active and inactive. I ...

Retrieving JSON data value without a key using AngularJS

I am struggling to retrieve a data value from a JSON array in Angular that does not have a key value. While I have come across examples of extracting values with keys, I haven't been able to crack this particular piece. The JSON returned from the API ...

What is the best way to choose a tag from an app within components or views?

In my main component file, I added a color picker input in the navigation section to change the background color of the application. This works fine as the body tag can be accessed easily within the DOM. Furthermore, I store the selected color in local st ...

Is the length of a complex match in Angular JS ng-if and ng-repeat greater than a specified value?

In my code, there is an ng-repeat that generates a table based on one loop, and within each row, another cell is populated based on a different loop: <tbody> <tr ng-repeat="r in roles | limitTo: 30"> <td>{{r.name}}</td> ...

I am seeking to modify the CSS of the parent component upon the loading of the child component in Angular

In my Angular blog, I have a root component with a navigation bar containing router links to create a single-page application. My goal is to darken the background around the link when the child component loads. Currently, the link highlights only when pres ...

Strategies for extracting special character codes from API data within a React functional component

I have been experimenting with a simple trivia API to improve my understanding of REACT. I have noticed that the question data is returning with special character codes, which is causing issues with react jsx. Can anyone suggest a method to easily convert ...

Steps to Extract the key value from the parseJson() function

Could someone assist me in retrieving the value of the "id_user" key from the script provided below? JSON.parse({ "data": [ { "id_user": "351023", "name": "", "age": "29", "link": "http://domain. ...

Is it true that document.execCommand only works with buttons and not links?

Below is the code snippet I am working with: <div contenteditable="true" style="height:100px;width:100px;border:1px solid; " class="editor"></div> <a class='bold' style="height:10px;width:10px;">B</a> $(function(){ ...

Strange behavior: JS object values disappear when accessed statically

I'm feeling puzzled. The issue at hand is that an object seems to lose its values within a loop based on the method of access. When accessed through variables, everything appears to be in order. However, when using static expressions identical to the ...

Out of Sync Promise Chain

I recently encountered an issue with promise chaining in JavaScript, specifically while working with Vue.js. Here is my code: I have an addItem function that inserts an item into the database. My goal is for this function to first insert the data into the ...

Traverse a computed attribute in Vue.js

I am working with a component that contains a simple array as its data: data() { return { roles: [ { id: '1' , name: 'admin'}, { id: '2' , name: 'user'}, { id: &a ...

The state initialization process is beginning with an [object Object] being passed into the setState function

Within the async function provided below, I am invoking stationData to verify that an array of objects is being passed into bartData (which currently consists of an empty array). Upon exploration, I have observed a response containing the anticipated array ...

Need help accessing data from an API using Axios.post and passing an ID?

Can someone help me with passing the ID of each item using Axios.Post in order to display its data on a single page? The image below in my Postman shows how I need to send the ID along with the request. Additionally, I have the first two URL requests for t ...

Is it possible to retrieve a variable from a geojson file using Vue 3 and Vite?

For my Vue 3 project, I am trying to import a variable from a geojson file. However, when I use import line from '@static/line.geojson', my page goes blank and it seems like Vue stops working. If I use import line from '@static/line.json&ap ...

Is there a way for me to direct to a different HTML page once I click on a certain data point?

At the moment, I have been utilizing this complete set of calendar codes. <!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <link href='fullcalendar.css' rel='stylesheet' /> <link href=&a ...

Exploring point clouds with three.js and NSF OpenTopography data: What's the best way to configure the camera and implement a controls library for seamless data navigation?

Currently, I am engaged in a small-scale project aimed at showcasing NSF OpenTopography data through a point cloud visualization using three.js. While I have successfully generated the point cloud, I am encountering significant challenges with setting up t ...

Using jQuery to decrease the size of a div element containing an image

Hello everyone! I have an image inside a div and I am currently moving it down at a specific time using a delay. My question is, how can I make the image shrink as it moves down the screen until it eventually disappears completely at a set location? Curre ...