مقدمة: لماذا يعتبر النوع الواحد مملًا؟
\n\nتخيل: أنت تكتب كودًا بلغة TypeScript وفجأة تواجه موقفًا حيث يجب على المصفوفة تخزين بيانات من أنواع مختلفة. على سبيل المثال، قائمة حيث كل عنصر إما string أو number. أو توبل (tuple) من اسم (نص) وعمر (رقم).
يتساءل العديد من المبتدئين (وليس فقط): «كيف أعلن عن مصفوفة بعدة أنواع في TypeScript؟» هذا السؤال حصد آلاف المشاهدات على Stack Overflow - وليس بدون سبب. TypeScript صارم مع الأنواع، لكنه في نفس الوقت يوفر أدوات مرنة لمثل هذه الحالات.
\n\nفي هذه المقالة، سنستعرض جميع الطرق للعمل مع المصفوفات ذات الأنواع المختلطة: من أنواع الاتحاد (union types) البسيطة إلى التوبل (tuples) والأدوية (generics). ستتعرف متى تستخدم أي نهج، وكيف لا تقع في فخ any.
1. أنواع الاتحاد (Union Types): الطريقة الأبسط
\n\nالخيار الأكثر وضوحًا هو استخدام union type (نوع الاتحاد). هذا عندما تقول: «يمكن للمصفوفة أن تحتوي على عناصر من نوع string أو number».
// مصفوفة يمكن أن تحتوي على نصوص أو أرقام\nconst mixedArray: (string | number)[] = ['hello', 42, 'world', 100];\n\n// كتابة بديلة (مكافئة)\nconst mixedArray2: Array<string | number> = ['hello', 42, 'world', 100];\n\nهنا (string | number)[] تعني: «مصفوفة، كل عنصر فيها هو نص أو رقم». سيتحقق TypeScript من أنك لا تحاول إضافة، على سبيل المثال، قيمة منطقية (boolean) أو كائن (object).
متى يكون هذا مناسبًا؟
\n- \n
- عندما لا يكون ترتيب العناصر مهمًا، بل المهم هو فقط مجموعة الأنواع المسموح بها. \n
- عندما تعمل مع API يعيد مصفوفة من بيانات غير متجانسة. \n
- عندما تخزن قائمة من المعرفات (أرقام) والأسماء (نصوص) في مصفوفة واحدة. \n
المأزق
\nعند استخدام أنواع الاتحاد، لا يمكنك ضمان أن في الموضع الأول سيكون نصًا وفي الموضع الثاني سيكون رقمًا. الكتابة (typing) تقول فقط: «هناك نصوص وأرقام في المصفوفة، لكن الترتيب غير معروف».
\n\nconst arr: (string | number)[] = [1, 'hello'];\n// TypeScript لا يعرف: arr[0] هو number أم string؟\nconst first = arr[0]; // النوع: string | number\n// لاستخدامه كرقم، تحتاج إلى تضييق النوع:\nif (typeof first === 'number') {\n console.log(first.toFixed(2));\n}\n\n2. التوبل (Tuples): عندما يكون الترتيب مهمًا
\n\nإذا كنت بحاجة إلى أن يكون للمصفوفة هيكل ثابت - على سبيل المثال، الأول نص، والثاني رقم، والثالث قيمة منطقية - استخدم التوبل (tuple types).
\n\n// توبل: [نص، رقم، منطقي]\nconst person: [string, number, boolean] = ['Alice', 30, true];\n\n// توبل مع تسميات (labels) - أكثر قابلية للقراءة\nconst product: [name: string, price: number, inStock: boolean] = ['Laptop', 1200, true];\n\nالتوبل تثبت بشكل صارم عدد العناصر والنوع في كل موضع. إذا حاولت تعيين قيمة من نوع خاطئ أو إضافة عنصر إضافي - سيعطي TypeScript خطأ.
\n\nعناصر Rest في التوبل
\nمنذ TypeScript 4.0، أصبح من الممكن تحديد عناصر rest في التوبل. هذا مفيد عندما يكون جزء من الهيكل ثابتًا والباقي متغيرًا:
\n\n// العنصر الأول نص، والباقي أرقام\nconst data: [string, ...number[]] = ['scores', 95, 87, 92];\n\n// يمكن الدمج\nconst logEntry: [timestamp: Date, ...messages: string[]] = [new Date(), 'info', 'user logged in'];\n\n3. Type Alias للمصفوفات المعقدة
\n\nعندما يصبح هيكل المصفوفة معقدًا، من المناسب استخراج النوع في alias (اسم مستعار) منفصل. هذا يحسن قابلية قراءة الكود ويسمح بإعادة استخدام النوع.
\n\ntype UserEntry = [id: number, name: string, email: string, isActive: boolean];\n\nconst users: UserE