تخطي إلى المحتوى الرئيسي

المعاملات الاختيارية (Optional Parameters) والمعاملات الافتراضية (Default Parameters) في TypeScript

المعاملات الاختيارية (Optional Parameters) والمعاملات الافتراضية (Default Parameters) في TypeScript

مقدمة عن المعاملات الاختيارية والمعاملات الافتراضية في TypeScript

في عالم البرمجة، تعتبر المعاملات (Parameters) جزءاً أساسياً من الدوال (Functions). ولكن ماذا لو كنا نرغب بتوفير خيارات أكثر للمبرمج عند تعريف دالة ما دون إلزامه بتمرير جميع القيم؟ هنا يأتي دور المعاملات الاختيارية (Optional Parameters) والمعاملات الافتراضية (Default Parameters).

في TypeScript و بيئة العمل، توفر هذه الميزات مرونة عالية للتعامل مع المدخلات المتغيرة، مع المحافظة على ميزة الأمان النوعي الصارم (Type Safety).

1. المعاملات الاختيارية (Optional Parameters)

المعاملات الاختيارية تتيح استدعاء الدالة مع إمكانية تجاهل بعض المعاملات، حيث تصبح قيمتها التلقائية undefined ما لم يتم التحقق منها أو تعيين بديل لها داخل جسم الدالة.

كيفية التعريف: يتم تحديد المعامل الاختياري بوضع علامة استفهام ? بعد اسم المعامل مباشرة وقبل النقطتين الرأسيتين للنوع:

function greet(name?: string, message?: string): void {
  const finalName = name || "زائر";
  const finalMsg = message || "أهلاً بك";
  console.log(`مرحبا ${finalName}! ${finalMsg}`);
}

// الاستدعاء بدون وسائط
greet(); // يطبع: مرحبا زائر! أهلاً بك

// الاستدعاء مع تمرير وسائط
greet("علي"); // يطبع: مرحبا علي! أهلاً بك
greet("علي", "صباح الخير"); // يطبع: مرحبا علي! صباح الخير

أمثلة عملية والتعامل مع فحص الأنواع (Narrowing)

عند استخدام المعاملات الاختيارية في العمليات الحسابية، يجب التحقق من وجود القيمة لتفادي الحصول على القيمة الخاصة NaN نتيجة ضرب أو جمع قيمة غير محددة (undefined):

// التحقق من المعامل الاختياري قبل المعالجة
function calculateArea(width: number, height?: number): number {
  if (height !== undefined) {
    return width * height;
  }
  return width * width; // حساب المساحة كمربع إذا لم يتم تمرير الارتفاع
}

console.log(calculateArea(10, 5)); // الناتج: 50
console.log(calculateArea(10));    // الناتج: 100

// دالة لمعالجة النصوص مع معامل اختياري
function getName(name?: string): string {
  return name || "غير معرف";
}

console.log(getName());       // الناتج: غير معرف
console.log(getName("علي"));  // الناتج: علي

2. المعاملات الافتراضية (Default Parameters)

تسمح المعاملات الافتراضية بتعريف قيم جاهزة تُستخدم تلقائياً عند استدعاء الدالة دون تقديم قيمة للمُعامل، أو عند تمرير القيمة undefined صراحةً.

طريقة التعريف: تُحدد المعاملات الافتراضية باستخدام علامة الإسناد = متبوعة بالقيمة الافتراضية المطلوبة، مع استنتاج النوع تلقائياً أو تحديده يدوياً:

function calculateRectangleArea(length: number = 0, width: number = 0): number {
    return length * width;
}

console.log(calculateRectangleArea(10, 5)); // الناتج: 50
console.log(calculateRectangleArea(10));    // الناتج: 0 (استخدام القيمة الافتراضية للـ width)
console.log(calculateRectangleArea());      // الناتج: 0

function greetUser(name: string, message: string = "مرحبا!"): void {
    console.log(`${message} ${name}`);
}

greetUser("سارة"); // الناتج: مرحبا! سارة
greetUser("سارة", "أهلاً بك يا"); // الناتج: أهلاً بك يا سارة

مقارنة بين المعاملات الاختيارية والمعاملات الافتراضية

على الرغم من تشابههما في إمكانية تجاهل تمرير المعامل أثناء الاستدعاء، إلا أن هناك فروقاً جوهرية في الاستخدام:

  • المعامل الاختياري (param?: type): تكون قيمته undefined إن لم يُمرر، ويتطلب من المطور فحص وجود القيمة يدوياً داخل الدالة.
  • المعامل الافتراضي (param: type = value): يوفر قيمة بديلة جاهزة تضمن استقرار العمليات دون الحاجة لفحص يدوي متكرر.
  • قاعدة الترتيب: يجب دائماً أن تأتي المعاملات الاختيارية في نهاية قائمة المعاملات، بينما المعاملات الافتراضية يمكن أن تسبق غيرها شرط تمرير undefined لتفعيل قيمتها الافتراضية.

تجاوز المعاملات الافتراضية وحالات الاستدعاء المختلفة

يستعرض المثال التالي كيف تتصرف لغة TypeScript عند تمرير قيم للمعاملات أو تركها لقيمها التلقائية:

function logDetails(
  param1: string = "افتراضي 1",
  param2: string = "افتراضي 2",
  param3?: number
): void {
  console.log(`param1: ${param1} | param2: ${param2} | param3: ${param3 ?? "غير محدد"}`);
}

// 1. تجاوز جميع المعاملات
logDetails("قيمة مخصصة 1", "قيمة مخصصة 2", 100);

// 2. استخدام القيم الافتراضية بالكامل
logDetails(); // الناتج: param1: افتراضي 1 | param2: افتراضي 2 | param3: غير محدد

// 3. تجاوز المعامل الأول فقط
logDetails("تجربة"); // الناتج: param1: تجربة | param2: افتراضي 2 | param3: غير محدد

// 4. تجاوز المعامل الثاني مع الاحتفاظ بالقيمة الافتراضية للأول عبر undefined
logDetails(undefined, "تجاوز الثاني فقط");

تجميع المعاملات باستخدام التفكيك (Destructuring)

عندما تكثر المعاملات الاختيارية والافتراضية داخل الدالة (3 معاملات فأكثر)، يصبح تمريرها مرتبة في سطر واحد أمراً معقداً وعرضة للأخطاء. الحل الأفضل والأكثر احترافية هو **تجميع المعاملات داخل كائن إعدادات واحد** باستخدام ميزة التفكيك (Object Destructuring):

// تعريف واجهة لخيارات الدالة
interface FunctionOptions {
    optionalText?: string;
    itemsList?: number[];
    isActive?: boolean;
    autoReload?: boolean;
}

// دالة تقبل كائن خيارات مجمع مع قيم افتراضية واضحة
function configureApp({
    optionalText = 'إعداد افتراضي',
    itemsList = [],
    isActive = false,
    autoReload = true
}: FunctionOptions = {}) {
    console.log(optionalText);
    console.log(itemsList);
    console.log(isActive);
    console.log(autoReload);
}

// استدعاء الدالة بمرونة تامة وبدون الاكتراث لترتيب المعاملات
configureApp();
configureApp({ isActive: true, optionalText: "تخصيص كامل" });

أمثلة وتطبيقات عملية شاملة

1. دالة الجمع المرن (Sum Function)

function sum(number1: number, number2?: number): number {
  if (number2 === undefined) {
    return number1;
  }
  return number1 + number2;
}

console.log(sum(5));     // الناتج: 5
console.log(sum(5, 10)); // الناتج: 15

2. دالة معالجة النصوص وعكسها (Reverse / Format String)

function formatText(str: string, reverse: boolean = false): string {
  if (reverse) {
    return str.split("").reverse().join("");
  }
  return str;
}

console.log(formatText("TypeScript"));        // الناتج: TypeScript
console.log(formatText("TypeScript", true));  // الناتج: tpircSepyT

3. دالة دمج النصوص الاختيارية (Append / Combine Text)

function combineText(text1: string, text2?: string): string {
  if (text2 === undefined) {
    return text1;
  }
  return `${text1} ${text2}`;
}

console.log(combineText("أهلاً"));          // الناتج: أهلاً
console.log(combineText("أهلاً", "بالعالم")); // الناتج: أهلاً بالعالم

الخلاصة

تُعد المعاملات الاختيارية الأداة المثلى لمنح الدوال القدرة على التكيف مع سيناريوهات الاستدعاء المختلفة بدون الحاجة لكتابة دوال متعددة (Overloads)، مما ينتج عنه واجهات برمجية (APIs) نظيفة وسهلة الاستخدام.

من جانب آخر، تقدم المعاملات الافتراضية حلاً أنيقاً لتوفير قيم بديلة جاهزة تمنع الأخطاء وتقلل من التعليمات البرمجية المتكررة (Boilerplate code). والجمع الذكي بينهما يمنحك التوازن المطلوب بين المرونة والصرامة النوعية لبناء مكونات برمجية قابلة لإعادة الاستخدام والتوسع بكل سهولة.

المراجع والمصادر


اكتشاف المزيد من كود التطور

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

MOHAMMAD ABU KASHREEF

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

اترك رد