How do you implement a custom error class in JavaScript?
Assesses fundamental understanding of JavaScript conventions, runtime behavior, and memory/performance considerations.
Hiring managers look for precision, avoidance of ambiguous jargon, and ability to explain trade-offs under real production conditions.
Custom error classes allow you to create specific error types with additional properties and methods, making error handling more precise.
Basic custom error:
class CustomError extends Error {
constructor(message) {
super(message);
this.name = 'CustomError';
}
}
throw new CustomError('Something went wrong');
Error with additional properties:
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
this.timestamp = new Date();
}
}
try {
throw new ValidationError('Invalid email', 'email');
} catch (error) {
if (error instanceof ValidationError) {
console.log(`${error.field}: ${error.message}`);
// email: Invalid email
}
}
HTTP error class:
class HTTPError extends Error {
constructor(message, status, response) {
super(message);
this.name = 'HTTPError';
this.status = status;
this.response = response;
}
get isClientError() {
return this.status >= 400 && this.status < 500;
}
get isServerError() {
return this.status >= 500;
}
}
async function fetchData(url) {
const response = await fetch(url);
if (!response.ok) {
throw new HTTPError(
'Failed to fetch',
response.status,
await response.json()
);
}
return response.json();
}
try {
await fetchData('/api/users');
} catch (error) {
if (error instanceof HTTPError && error.isClientError) {
console.log('Client error:', error.message);
}
}
Error factory pattern:
class AppError extends Error {
constructor(message, code, metadata = {}) {
super(message);
this.name = 'AppError';
this.code = code;
this.metadata = metadata;
}
static badRequest(message, metadata) {
return new AppError(message, 'BAD_REQUEST', metadata);
}
static notFound(resource) {
return new AppError(
`${resource} not found`,
'NOT_FOUND',
{ resource }
);
}
static unauthorized(message) {
return new AppError(message, 'UNAUTHORIZED');
}
}
throw AppError.notFound('User');
throw AppError.badRequest('Invalid input', { field: 'email' });
Error with stack trace customization:
class DatabaseError extends Error {
constructor(message, query) {
super(message);
this.name = 'DatabaseError';
this.query = query;
// Maintain proper stack trace
if (Error.captureStackTrace) {
Error.captureStackTrace(this, DatabaseError);
}
}
}
Candidate Response Strategy & Interview Tips
- Start with a concise one-sentence summary: Deliver a direct, confident answer first before expanding into nuances.
- Demonstrate real-world trade-offs: Discuss where this approach excels and when you would avoid it in production systems.
- Discuss complexity & edge cases: Proactively explain time/space complexity or boundary conditions (null values, scale limits).
- Prepare for interviewer follow-ups: Technical hiring panels frequently probe deeper into concurrency, backward compatibility, or alternative libraries.