Generics في JavaScript: لماذا هي ضرورية وكيفية استخدامها
التطوير الحديث في JavaScript و Node.js لا يتطلب فقط كتابة سريعة للكود، بل يتطلب أيضاً موثوقيته. من الأدوات الرئيسية التي توفر المرونة وأمان الأنواع هي الـ Generics (الأدوية). في هذه المقالة، سنشرح ما هي الـ Generics، ولماذا هي ضرورية في نظام JavaScript البيئي، وكيفية تطبيقها بفعالية في الممارسة العملية. ستكون المادة مفيدة لكل من المبتدئين والمطورين ذوي الخبرة الذين يرغبون في تعميق معرفتهم.
ما هي Generics ولماذا هي ضرورية؟
Generics (الأنواع العامة) هي آلية تسمح بإنشاء مكونات (دوال، كلاسات، واجهات) تعمل مع أنواع مختلفة من البيانات، مع الحفاظ على كتابة صارمة للأنواع. على عكس JavaScript العادية، وهي لغة ديناميكية الأنواع، تأتي الـ Generics من عالم TypeScript - وهي إضافة فوق JS تضيف كتابة ثابتة للأنواع.
لماذا هذا ضروري؟ تخيل أنك تكتب دالة تقوم بإرجاع العنصر الأول من مصفوفة. بدون Generics، سيتعين عليك استخدام النوع any، مما يحرمك من التلميحات من بيئة التطوير ويزيد من خطر الأخطاء. مع Generics، يمكنك كتابة كود عام يعمل بأمان مع الأرقام، والسلاسل النصية، والكائنات، وأي أنواع أخرى.
// بدون Generics (غير آمن)function getFirst(arr: any[]): any { return arr[0];}
// مع Generics (آمن)function getFirst<T>(arr: T[]): T | undefined { return arr[0];}
const num = getFirst([1, 2, 3]); // النوع numberconst str = getFirst(['a', 'b']); // النوع stringوبالتالي، تجعل الـ Generics الكود أكثر قابلية لإعادة الاستخدام و أماناً للأنواع، وهو أمر مهم بشكل خاص في المشاريع الكبيرة على Node.js.
كيفية استخدام Generics في TypeScript (JavaScript مع الأنواع)
على الرغم من أن JavaScript الخالص لا يدعم الـ Generics، إلا أن TypeScript يتكامل بالكامل مع نظام Node.js البيئي. دعنا نستعرض السيناريوهات الرئيسية للاستخدام.
1. الدوال العامة (Generic Functions)
أبسط طريقة هي جعل الدالة معلمة بنوع واحد أو أكثر. هذا مفيد للأدوات المساعدة التي تعمل مع المجموعات.
// دالة لدمج مصفوفتينfunction mergeArrays<T>(arr1: T[], arr2: T[]): T[] { return [...arr1, ...arr2];}
const numbers = mergeArrays([1, 2], [3, 4]); // number[]const strings = mergeArrays(['hello'], ['world']); // string[]هنا <T> هو معامل نوع يتم استبداله تلقائياً عند استدعاء الدالة. يمكنك أيضاً تحديد النوع بشكل صريح:
const mixed = mergeArrays<number | string>([1], ['a']); // (number | string)[]2. الكلاسات العامة (Generic Classes)
الـ Generics مناسبة تماماً لإنشاء هياكل بيانات مثل الأكوام (Stacks)، والطوابير (Queues)، أو التخزين المؤقت (Caches).
class Stack<T> { private items: T[] = [];
push(item: T): void { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
peek(): T | undefined { return this.items[this.items.length - 1]; }}
// الاستخدامconst numberStack = new Stack<number>();numberStack.push(10);numberStack.push(20);console.log(numberStack.pop()); // 20
const stringStack = new Stack<string>();stringStack.push('TypeScript');console.log(stringStack.peek()); // 'TypeScript'3. قيود الأنواع (Constraints)
أحياناً تحتاج إلى تقييد الـ Generic بمجموعة معينة من الأنواع. على سبيل المثال، ضمان أن الكائن يحتوي على خاصية length.
// نقيد T بالأنواع التي لديها lengthfunction logLength<T extends { length: number }>(item: T): void { console.log(`الطول: ${item.length}`);}
logLength('مرحباً'); // 6logLength([1, 2, 3]); // 3// logLength(123); // خطأ: number ليس لديه lengthالكلمة المفتاحية extends