How to extend Error with custom properties?
1. The main idea
You create your own class that inherits from Error,
and add the properties you need to it - for example: code, status, details, cause, context, and so on.
2. Example: a basic custom error
javascript
class AppError extends Error {
constructor(message, code) {
super(message); // pass the message to the parent (Error)
this.name = "AppError"; // set the error's name
this.code = code; // add our own property
}
}
try {
throw new AppError("Something went wrong", "SERVER_ERROR");
} catch (e) {
console.log(e.name); // AppError
console.log(e.message); // Something went wrong
console.log(e.code); // SERVER_ERROR
console.log(e.stack); // Call stack
}Now your error has both the standard properties (message, stack) and custom ones (code).
3. Example: adding an HTTP code and context
This is convenient for APIs, especially in NestJS / Express:
javascript
class HttpError extends Error {
constructor(message, status = 500, details = {}) {
super(message);
this.name = this.constructor.name;
this.status = status;
this.details = details;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
throw new HttpError("User not found", 404, { userId: 123 });When handling the error:
javascript
try {
throw new HttpError("User not found", 404, { userId: 123 });
} catch (err) {
console.log(err.name); // HttpError
console.log(err.status); // 404
console.log(err.details); // { userId: 123 }
}4. Example: a hierarchy of custom errors
You can build a whole "tree" of errors:
javascript
class AppError extends Error {
constructor(message, code) {
super(message);
this.name = this.constructor.name;
this.code = code;
Error.captureStackTrace?.(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message, field) {
super(message, "VALIDATION_ERROR");
this.field = field;
}
}
class AuthError extends AppError {
constructor(message = "Unauthorized") {
super(message, "AUTH_ERROR");
this.status = 401;
}
}Usage:
javascript
try {
throw new ValidationError("Invalid email", "email");
} catch (err) {
if (err instanceof ValidationError) {
console.log("Error in field:", err.field);
}
console.log(err.code); // VALIDATION_ERROR
}5. Example with cause (ES2022+)
The modern ECMAScript standard supports the cause property,
to store the original error that led to the current one.
javascript
try {
JSON.parse("invalid JSON");
} catch (parseErr) {
throw new AppError("Error parsing JSON", "PARSE_ERROR", { cause: parseErr });
}To support cause, you need to add it to the constructor:
javascript
class AppError extends Error {
constructor(message, code, options = {}) {
super(message, options);
this.name = this.constructor.name;
this.code = code;
this.cause = options.cause;
}
}6. Example of usage in production (Express / NestJS)
javascript
app.use((err, req, res, next) => {
if (err instanceof AppError) {
res.status(err.status || 500).json({
error: err.name,
message: err.message,
code: err.code,
details: err.details,
});
} else {
res.status(500).json({ error: "InternalError", message: "Unknown error" });
}
});7. Why extend Error
| Reason | Example |
|---|---|
| Clear, understandable messages | "Email is required" instead of "Unexpected token" |
| Structure and typing | ValidationError, AuthError, HttpError |
| Convenient logging | You can include code, status, details |
| Storing the original cause | via cause |
| Easier debugging | Different error types are easy to tell apart in catch |
Summary
To extend Error with your own properties:
javascript
class MyError extends Error {
constructor(message, customProp) {
super(message);
this.name = "MyError";
this.customProp = customProp;
Error.captureStackTrace?.(this, this.constructor);
}
}And use it:
javascript
throw new MyError("Database error", { query: "SELECT * FROM users" });Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.