TypeScript: تمرير معاملات اختيارية مع تخطي أخرى

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

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

\n

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

\n

على Stack Overflow، جمع هذا السؤال آلاف المشاهدات. مطورو TypeScript يتعثرون باستمرار في نفس المشكلة: كيف تمرر معاملًا اختياريًا واحدًا، متخطيًا آخر؟ لأن الصيغة القياسية لا تسمح بذلك – يتم تمرير المعاملات حسب الموضع وليس حسب الاسم.

\n

اليوم سنحل هذه المشكلة مرة واحدة وإلى الأبد. سأريكم 4 طرق عملية لحل المشكلة: من إعادة الهيكلة البسيطة إلى التقنيات المتقدمة مع كائنات partial. هيا بنا!

\n\n

لماذا هذه مشكلة أساسًا؟ اللعنة الموضعية

\n

في TypeScript (كما في JavaScript)، يتم تمرير معاملات الدالة بالترتيب. إذا كان لديك دالة:

\n
function createUser(\n  name: string,\n  age?: number,\n  email?: string,\n  isAdmin?: boolean\n) { /* ... */ }
\n

لا يمكنك استدعاؤها هكذا: createUser('John', , 'john@mail.com'). تخطي المعامل يترك فراغًا لا يستطيع TypeScript التعامل معه. سيتعين عليك تمرير undefined أو null، مما يلوث الكود والمنطق.

\n

لكن هناك خبر سار: يمكننا تجاوز هذا القيد بعدة طرق أنيقة. دعنا نستعرض كلًا منها.

\n\n

الطريقة 1: كائن المعاملات (الأكثر صحة)

\n

هذا هو النمط الموصى به في المشاريع الجادة. بدلاً من مجموعة من المعاملات الموضعية، نستخدم كائنًا واحدًا بحقول اختيارية:

\n
interface UserOptions {\n  name: string;\n  age?: number;\n  email?: string;\n  isAdmin?: boolean;\n}\n\nfunction createUser(options: UserOptions): void {\n  const { name, age, email, isAdmin } = options;\n  console.log(`تم إنشاء المستخدم ${name}، العمر: ${age ?? 'غير محدد'}`);\n}\n\n// الآن يمكنك تمرير ما تحتاجه فقط!\ncreateUser({ name: 'آنا', email: 'anna@mail.com' });\ncreateUser({ name: 'بيتر', isAdmin: true });\n
\n

المزايا واضحة:

\n
    \n
  • لا يهم عدد المعاملات – نمرر فقط ما نحتاجه
  • \n
  • الكود موثق ذاتيًا: يظهر فورًا ما هو المسؤول عن ماذا
  • \n
  • سهولة إضافة حقول جديدة دون تغيير التوقيع
  • \n
\n

هذا الأسلوب يحل 90% من مشاكل المعاملات الاختيارية. لكن إذا كنت تعمل مع نظام قديم أو لا يمكنك تغيير API – تابع القراءة.

\n\n

الطريقة 2: التحميل الزائد للدوال (function overloading)

\n

يسمح TypeScript بتعريف عدة تواقيع لدالة واحدة. هذا مفيد عندما يكون لديك مجموعات محددة من المعاملات:

\n
// تعريف التحميلات الزائدة\nfunction getData(url: string): Promise<any>;\nfunction getData(url: string, timeout: number): Promise<any>;\nfunction getData(url: string, timeout: number, retries: number): Promise<any>;\n\n// التنفيذ (يجب أن يكون الأكثر عمومية)\nfunction getData(\n  url: string,\n  timeout?: number,\n  retries?: number\n): Promise<any> {\n  // المنطق\n  return fetch(url, { signal: AbortSignal.timeout(timeout ?? 5000) });\n}\n\n// الآن يمكنك الاستدعاء هكذا:\ngetData('/api/users');               // timeout = 5000 (افتراضي)\ngetData('/api/users', 3000);         // timeout = 3000\n// لكن هكذا – لا يمكن (تخطي timeout غير مسموح بدون تحميل زائد):\n// getData('/api/users', undefined, 2); // سيء\n
\n

العيب: التحميل الزائد لا يحل مشكلة تخطي المعاملات الوسطى. إنها تعطي فقط مجموعات صارمة. للمرونة، استخدم الطريقة الأولى.

\n\n

الطريقة 3: معاملات rest + التفكيك (الغريبة)

\n

إذا كنت تريد الحفاظ على الموضعية ولكن إضافة مرونة، يمكنك استخدام tuples مع rest:

\n
type OptionalParams = [number, string?, boolean?];\n\nfunction processData(\n  required: string,\n  ...opt                

المدونات

توصيات الكتب