How do you create a custom error type?
Basic template (ES2022+)
javascript
class AppError extends Error {
constructor(message, { code, cause } = {}) {
super(message, { cause }); // preserves stack and cause
this.name = this.constructor.name; // "AppError"
this.code = code ?? "APP_ERROR";
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
// Specific cases
class ValidationError extends AppError {
constructor(message, details, opts = {}) {
super(message, { ...opts, code: "VALIDATION_ERROR" });
this.details = details; // any fields you need
}
}
class AuthError extends AppError {
constructor(message = "Unauthorized", opts = {}) {
super(message, { ...opts, code: "AUTH_ERROR" });
this.status = 401;
}
}Usage:
javascript
function parseUser(input) {
if (!input.email) {
throw new ValidationError("Email is required", { field: "email" });
}
return input;
}
try {
parseUser({});
} catch (e) {
if (e instanceof ValidationError) {
console.log(e.code, e.details); // VALIDATION_ERROR { field: "email" }
} else {
console.error("Unexpected error", e);
}
}Wrapping the original error (cause)
Useful when catching low-level exceptions:
javascript
try {
JSON.parse("invalid JSON");
} catch (e) {
throw new AppError("Failed to parse the config", { cause: e, code: "CONFIG_PARSE" });
}Later you can inspect the chain:
javascript
console.error(err.cause); // the original errorTypeScript version
javascript
type AppErrorCode = "APP_ERROR" | "VALIDATION_ERROR" | "AUTH_ERROR";
class AppError extends Error {
code: AppErrorCode;
declare cause?: unknown; // so TS knows about cause
constructor(message: string, opts: { code?: AppErrorCode; cause?: unknown } = {}) {
super(message, { cause: opts.cause });
this.name = new.target.name;
this.code = opts.code ?? "APP_ERROR";
if ((Error as any).captureStackTrace) {
(Error as any).captureStackTrace(this, new.target);
}
}
}
class ValidationError extends AppError {
details?: Record<string, unknown>;
constructor(message: string, details?: Record<string, unknown>, opts: { cause?: unknown } = {}) {
super(message, { ...opts, code: "VALIDATION_ERROR" });
this.details = details;
}
}A few practical tips
-
Serialization (logging/API response):
javascriptfunction errorToJson(err) { return { name: err.name, message: err.message, code: err.code, stack: err.stack, details: err.details, cause: err.cause instanceof Error ? err.cause.message : err.cause, }; } -
HTTP mapping: Add a
status(400/401/404/500) to your errors and map them to responses at the middleware level. -
Guarantee
instanceof: Modern engines are fine, but theextends Error+captureStackTrace(when available) pattern is a reliable base.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.