ساخت خطاهای سفارشی با ارث‌بری از Error

در درس‌های قبل با انواع خطای داخلی جاوااسکریپت مثل TypeError و RangeError آشنا شدید. اما در پروژه‌های واقعی، اغلب لازم است خطاهایی متناسب با منطق خاص همان برنامه تعریف شوند؛ مثلاً خطای «موجودی ناکافی» در یک فروشگاه، یا خطای «دسترسی غیرمجاز» در یک سیستم احراز هویت. جاوااسکریپت امکان ساخت این نوع خطاهای سفارشی را از طریق ارث‌بری کلاسی از Error فراهم می‌کند.

ساخت ساده‌ترین خطای سفارشی

برای ساخت یک نوع خطای سفارشی، کافی است کلاسی تعریف شود که از Error ارث‌بری کند و در سازنده‌ی خود، پیام خطا را به سازنده‌ی والد منتقل کند:

class InsufficientStockError extends Error {
  constructor(message) {
    super(message); // فراخوانی سازنده‌ی Error برای تنظیم message و stack
    this.name = "InsufficientStockError";
  }
}

throw new InsufficientStockError("موجودی کافی برای این سفارش وجود ندارد");

تنظیم صریح this.name ضروری است، چون بدون آن، مقدار name همان مقدار پیش‌فرض یعنی "Error" باقی می‌ماند و امکان تشخیص نوع دقیق خطا از طریق ویژگی name از بین می‌رود.

افزودن اطلاعات اضافی به خطای سفارشی

یکی از مزیت‌های اصلی خطای سفارشی این است که می‌توان علاوه بر پیام، اطلاعات اضافی و مرتبط با همان خطا را نیز در خود شیء ذخیره کرد:

خطای سفارشی با اطلاعات اضافی
خروجی کنسول اینجا نمایش داده می‌شود...

در این مثال، ویژگی‌های productName، requested و available فراتر از یک پیام متنی ساده، اطلاعات ساختاریافته‌ای در اختیار بلوک catch قرار می‌دهند که امکان واکنش دقیق‌تر و برنامه‌نویسی‌شده‌تر به خطا را فراهم می‌کند.

سلسله‌مراتب چند خطای سفارشی

در پروژه‌های بزرگ‌تر، معمولاً چند نوع خطای سفارشی مرتبط با هم تعریف می‌شوند که می‌توانند خودشان نیز از یک کلاس پایه‌ی مشترک ارث‌بری کنند:

class AppError extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
  }
}

class ValidationError extends AppError {}
class NotFoundError extends AppError {}
class AuthenticationError extends AppError {}

function findUser(id) {
  if (!id) {
    throw new ValidationError("شناسه‌ی کاربر الزامی است");
  }
  // فرض کنید کاربر پیدا نشد
  throw new NotFoundError("کاربر مورد نظر یافت نشد");
}

نکته‌ی جالب در این الگو، استفاده از this.constructor.name است که به‌طور خودکار نام همان کلاسی که واقعاً نمونه‌سازی شده را برمی‌گرداند (مثلاً "ValidationError")، بدون نیاز به نوشتن this.name جداگانه در هر کلاس فرزند.

بررسی خطای سفارشی با instanceof

از آنجا که این کلاس‌ها با extends تعریف شده‌اند، عملگر instanceof دقیقاً همان‌طور که برای خطاهای داخلی کار می‌کرد، برای تشخیص نوع خطای سفارشی نیز قابل استفاده است:

try {
  findUser(null);
} catch (error) {
  if (error instanceof ValidationError) {
    console.log("خطای اعتبارسنجی:", error.message);
  } else if (error instanceof NotFoundError) {
    console.log("یافت نشد:", error.message);
  } else if (error instanceof AppError) {
    console.log("خطای عمومی برنامه:", error.message);
  } else {
    throw error; // خطای کاملاً ناشناخته، به بیرون منتقل می‌شود
  }
}

نکات کلیدی

  • یک خطای سفارشی با ارث‌بری کلاسی از Error و فراخوانی super(message) در سازنده ساخته می‌شود.
  • تنظیم صریح this.name ضروری است، وگرنه مقدار پیش‌فرض "Error" باقی می‌ماند.
  • خطای سفارشی می‌تواند علاوه بر پیام، ویژگی‌های اضافی و ساختاریافته‌ای درباره‌ی همان خطا نگه دارد.
  • در سلسله‌مراتب چند خطای سفارشی، this.constructor.name نام دقیق کلاس نمونه‌سازی‌شده را بدون تکرار کد فراهم می‌کند.

جمع‌بندی

خطاهای سفارشی، با ارث‌بری از Error، امکان تعریف انواع خطای متناسب با منطق واقعی هر برنامه را فراهم می‌کنند و به‌جای پیام‌های متنی ساده، اطلاعات ساختاریافته و قابل‌بررسی در اختیار بلوک catch قرار می‌دهند. ترکیب این خطاها با instanceof، پایه‌ی طراحی سیستم‌های مدیریت خطای مقیاس‌پذیر و قابل‌نگهداری در پروژه‌های واقعی است.