TypeScript: معالجة الأخطاء في TypeScript

آخر تحديث: 2026-08-26

تعد معالجة الأخطاء جزءًا أساسيًا من أي مشروع — ويجعل نظام الأنواع في TypeScript معالجة الأخطاء أكثر أمانًا ويمكن التنبؤ بها بشكل أفضل.

1. أساسيات معالجة الأخطاء في جافا سكريبت

(1) try/catch/finally

TYPESCRIPT
try {
  let data = JSON.parse(input);
  console.log(data);
} catch (error) {
  // error The type is unknown(strict In mode)
  console.log("Parsing Failed");
} finally {
  console.log("Free Up Resources");
  // This will be executed regardless of success or failure
}

(2) عبارة throw

يمكن لـ JavaScript إرجاع أي قيمة — وليس فقط كائنات Error:

TYPESCRIPT
throw new Error("An error occurred");        // ✅ Standard Practice
throw "An error occurred";                    // ⚠️ It's possible, but not ideal.
throw 404;                         // ⚠️ It's possible, but not ideal.
throw { code: 500, msg: "Server Error" }; // ⚠️ It's possible, but not ideal.
📌 توصية: احرص دائمًا على إلقاء كائن Error (أو إحدى فئاته الفرعية) — فهو يحمل معلومات مكدس الاستدعاءات، مما يسهل عملية تصحيح الأخطاء. أما إلقاء سلسلة نصية أو رقم فلا يوفر معلومات مكدس الاستدعاءات.

(3) أنواع الأخطاء

TYPESCRIPT
interface Error {
  name: string;     // Error name (e.g., "TypeError", "RangeError")
  message: string;  // Error Description
  stack?: string;   // Call Stack(Non-standard but widely supported)
}


2. أنواع الأخطاء المضمنة وتضييق النطاق

(1) الأخطاء الشائعة المضمنة

نوع الخطأ سبب حدوثه
Error خطأ عام
TypeError خطأ في الكتابة (مثل: null.toString())
RangeError القيمة خارج النطاق (مثل: تجاوز السعة التكراري)
SyntaxError خطأ في بناء الجملة (مثل: فشل JSON.parse)
ReferenceError إشارة إلى متغير غير مُعرَّف
URIError خطأ في ترميز/فك ترميز URI

(2) تضييق نطاق أنواع الأخطاء باستخدام instanceof

TYPESCRIPT
function processValue(value: unknown) {
  try {
    let num = Number(value);
    if (isNaN(num)) throw new TypeError("Not a valid number");
    if (num < 0) throw new RangeError("Cannot be a negative number");
    return num;
  } catch (error) {
    if (error instanceof TypeError) {
      console.log(`Type error:${error.message}`);  // ✅ narrowed to TypeError
    } else if (error instanceof RangeError) {
      console.log(`Range error:${error.message}`);  // ✅ narrowed to RangeError
    } else if (error instanceof Error) {
      console.log(`Other Errors:${error.message}`);  // ✅ narrowed to Error
    } else {
      // In theory, it won't reach there.——JavaScript Always throw Error or its subclasses
      console.log("Unknown error");
    }
  }
}


3. فئات الأخطاء المخصصة

(1) الأخطاء الأساسية المخصصة

TYPESCRIPT
class AppError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "AppError";  // Incorrect setting name
  }
}

class ValidationError extends AppError {
  constructor(
    message: string,
    public field: string    // Additional error messages
  ) {
    super(message);
    this.name = "ValidationError";
  }
}

class NotFoundError extends AppError {
  constructor(
    public resource: string,
    public id: number | string
  ) {
    super(`${resource} (${id}) Does not exist`);
    this.name = "NotFoundError";
  }
}

(2) استخدام الأخطاء المخصصة

TYPESCRIPT
function findUser(id: number) {
  if (id <= 0) {
    throw new ValidationError("IDMust be a positive number", "id");
  }
  // Simulated Search
  if (id > 100) {
    throw new NotFoundError("User", id);
  }
  return { id, name: "User" + id };
}

try {
  let user = findUser(999);
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(`Verification Failed:Field ${error.field},${error.message}`);
  } else if (error instanceof NotFoundError) {
    console.log(`Not found:${error.resource},ID=${error.id}`);
  } else if (error instanceof Error) {
    console.log(`Error:${error.message}`);
  }
}

▶ مثال: نظام أخطاء HTTP

TYPESCRIPT 📖 للعرض فقط
class HttpError extends Error {
  constructor(
    public statusCode: number,
    message: string
  ) {
    super(message);
    this.name = "HttpError";
  }
}

class BadRequestError extends HttpError {
  constructor(message: string) {
    super(400, message);
    this.name = "BadRequestError";
  }
}

class UnauthorizedError extends HttpError {
  constructor(message: string = "Unauthorized") {
    super(401, message);
    this.name = "UnauthorizedError";
  }
}

class ForbiddenError extends HttpError {
  constructor(message: string = "Access Denied") {
    super(403, message);
    this.name = "ForbiddenError";
  }
}

class NotFoundError2 extends HttpError {
  constructor(resource: string) {
    super(404, `${resource} Does not exist`);
    this.name = "NotFoundError";
  }
}

// Usage
function handleRequest(path: string) {
  if (!path.startsWith("/api/")) {
    throw new BadRequestError("The path must begin with /api/ Introduction");
  }
  if (path === "/api/admin") {
    throw new ForbiddenError();
  }
  return { data: "Response Data" };
}

try {
  let result = handleRequest("/api/users");
  console.log(result.data);
} catch (error) {
  if (error instanceof HttpError) {
    console.log(`HTTP ${error.statusCode}: ${error.message}`);
  }
}
50 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

الناتج:

TEXT 📖 للعرض فقط
Response Data


4. نمط النتائج

تكمن مشكلة آلية try/catch في أن «الاستثناءات تكون ضمنية» — أي أن توقيع الدالة لا يشير إلى احتمال قيامها بإلقاء استثناء. أما نمط Result فيجعل الأخطاء جزءًا من قيمة الإرجاع:

(1) تحديد نوع النتيجة

TYPESCRIPT
type Success<T> = { ok: true; value: T };
type Failure<E> = { ok: false; error: E };
type Result<T, E = Error> = Success<T> | Failure<E>;

(2) إنشاء النتيجة

TYPESCRIPT
function success<T>(value: T): Success<T> {
  return { ok: true, value };
}

function failure<E>(error: E): Failure<E> {
  return { ok: false, error };
}

(3) استخدام Result بدلاً من try/catch

TYPESCRIPT
function divide(a: number, b: number): Result<number, string> {
  if (b === 0) {
    return failure("The divisor cannot be zero.");
  }
  return success(a / b);
}

let result1 = divide(10, 2);
let result2 = divide(10, 0);

if (result1.ok) {
  console.log(result1.value);  // 5 ✅
} else {
  console.log(result1.error);
}

if (result2.ok) {
  console.log(result2.value);
} else {
  console.log(result2.error);  // "The divisor cannot be zero." ✅
}

(4) النتيجة: الطرق القائمة على الأدوات

TYPESCRIPT
function tryCatch<T>(fn: () => T): Result<T, Error> {
  try {
    return success(fn());
  } catch (error) {
    return failure(error instanceof Error ? error : new Error(String(error)));
  }
}

async function tryAsync<T>(fn: () => Promise<T>): Promise<Result<T, Error>> {
  try {
    return success(await fn());
  } catch (error) {
    return failure(error instanceof Error ? error : new Error(String(error)));
  }
}

// Usage
let parseResult = tryCatch(() => JSON.parse('{"name":"Charlie"}'));
if (parseResult.ok) {
  console.log(parseResult.value.name);  // "Charlie"
}


5. التعامل مع أنواع الأخطاء في كتلة catch

(1) في الوضع الصارم، يُعتبر الخطأ غير معروف

TYPESCRIPT
try {
  JSON.parse("invalid");
} catch (error) {
  // error The type is unknown(TypeScript 4.4+)
  // console.log(error.message);  // ❌ unknown NonemessageProperties

  // ✅ Method 1:instanceof Inspection
  if (error instanceof Error) {
    console.log(error.message);
  }

  // ✅ Method 2:Type Assertion(Caution)
  let msg = (error as Error).message;

  // ✅ Method 3:Type Guard Function
  function getErrorMessage(error: unknown): string {
    if (error instanceof Error) return error.message;
    if (typeof error === "string") return error;
    return "Unknown error";
  }
  console.log(getErrorMessage(error));
}

(2) النوع error في الإصدارات القديمة من TypeScript

TYPESCRIPT
// TypeScript 4.3 and earlier——catch 's error The type is any
// You can tsconfig Useful useUnknownInCatchVariables: true Change to unknown

// TypeScript 4.4+——Default unknown(strict In mode)

▶ مثال: تضييق أنواع الأخطاء غير المعروفة

الناتج:

TEXT 📖 للعرض فقط
Division by zero
TYPESCRIPT
function safeReadFile(path: string): string | null {
  try {
    let content = "[simulated file content]";
    if (Math.random() > 0.5) throw new SyntaxError("Parse error");
    return content;
  } catch (error: unknown) {
    if (error instanceof SyntaxError) {
      console.log(`Syntax issue: ${error.message}`);
    } else if (error instanceof Error) {
      console.log(`Error: ${error.message}`);
    } else {
      console.log("Unknown error occurred");
    }
    return null;
  }
}

let data = safeReadFile("config.json");

الناتج:

TEXT 📖 للعرض فقط
Division by zero

▶ مثال: حارس نوع لمعالجة الأخطاء

الناتج:

TEXT 📖 للعرض فقط
Division by zero
TYPESCRIPT
function isError(value: unknown): value is Error {
  return value instanceof Error;
}

function getErrorMessage(error: unknown): string {
  if (isError(error)) return error.message;
  if (typeof error === "string") return error;
  if (typeof error === "number") return `Error code: ${error}`;
  return "An unknown error occurred";
}

type AppResult<T> = { ok: true; value: T } | { ok: false; error: string };

function divide(a: number, b: number): AppResult<number> {
  if (b === 0) return { ok: false, error: "Division by zero" };
  return { ok: true, value: a / b };
}

let r = divide(10, 0);
if (!r.ok) console.log(getErrorMessage(r.error)); // Division by zero

الناتج:

TEXT 📖 للعرض فقط
Division by zero

❓ أسئلة شائعة

س أيهما يجب أن أستخدم، try/catch أم نمط Result؟
ج هاتان الطريقتان لا تستبعد إحداهما الأخرى. try/catch هي آلية معالجة الاستثناءات القياسية في JavaScript/TypeScript — وهي مناسبة للأخطاء غير المتوقعة في وقت التشغيل (مثل أعطال الشبكة أو مشكلات تحليل JSON). أما نمط Result فهو مناسب للأخطاء التشغيلية المتوقعة — حيث تنص توقيعات الدالة صراحةً على أن «الفشل ممكن»، ويجب على المستدعي معالجته. ما عليك سوى الحفاظ على أسلوب متسق في جميع أنحاء مشروعك.
س لماذا تم التقاط الخطأ بواسطة catch وunknown بدلاً من Error؟
ج لأن لغة JavaScript تسمح بإلقاء أي قيمة — فكل من throw "message" وthrow 42 قيمتان صالحتان. لا يمكن لـ TypeScript ضمان أن تكون القيمة التي يتم التقاطها بواسطة catch مثيلًا لـ Error، لذا فإن استخدام unknown هو النهج الأكثر أمانًا. في التطوير الفعلي، 99% من حالات الرمي هي كائنات Error — يمكنك تضييق نطاق البحث باستخدام instanceof Error.
س هل يجب على فئة الأخطاء المخصصة استدعاء super؟
ج نعم، يجب ذلك. عندما ترث فئة الأخطاء المخصصة من Error، يجب أن تستدعي super(message) في منشئ الفئة لتهيئة الخاصية message الخاصة بـ Error. يُنصح أيضًا بتعيين this.name يدويًّا — وإلا، فسيتم تعيين name افتراضيًّا على اسم الفئة الأم.
س هل يؤثر الكود الموجود في كتلة finally على قيمة الإرجاع؟
ج إن عبارة return الموجودة في كتلة finally تلغي تأثير عبارة return الموجودة في كتلة try/catch — وهذا مصدر شائع للأخطاء. يُنصح باستخدام كتلة finally فقط لتنظيف الموارد (إغلاق الملفات، وإلغاء القفل، وما إلى ذلك) وعدم تضمين عبارات return فيها.

📖 ملخص

📝 تمارين

  1. المشكلة الأساسية (الصعوبة ⭐): اكتب دالة safeParse(json: string): Result<object, Error> تقوم بتغليف JSON.parse في كتلة try/catch وتُرجع success في حالة النجاح وfailure في حالة الفشل.
  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): عرّف الفئة الأساسية AppError والفئات الفرعية DatabaseError (التي تحتوي على خاصية query) وAuthError (التي تحتوي على خاصية statusCode). اكتب دالة تُرجع رموز حالة HTTP ورسائل مختلفة بناءً على نوع الخطأ.
  3. التحدي (الصعوبة: ⭐⭐⭐): قم بتنفيذ tryAsync<T>(fn: () => Promise<T>): Promise<Result<T, AppError>> لتحويل الاستثناءات الناتجة عن العمليات غير المتزامنة بشكل موحد إلى نظام AppError (مع التمييز بين أخطاء الشبكة وأخطاء انتهاء المهلة وأخطاء الخادم). اكتب دالة fetchWithRetry تعيد المحاولة تلقائيًا في حالة الفشل.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%