🚀 احتراف Generics في TypeScript: المرونة والدقة في آن واحد
اليوم سنناقش واحداً من أهم المفاهيم التي تجعل لغة TypeScript قوية جداً ومحبوبة من قبل المطورين، وهي الـ Generics.
إذا كنت قد تعلمت الأنواع (Types) الأساسية، فربما لاحظت أنك أحياناً تريد كتابة دالة تعمل مع "أي نوع" من البيانات، ولكن دون أن تفقد ميزة "فحص الأنواع". هنا يأتي دور الـ Generics.
❓ ما هي الـ Generics ولماذا نحتاجها؟
ببساطة، الـ Generics هي وسيلة تسمح لنا بإنشاء "قالب" (Template) للدالة أو الكلاس، بحيث لا نحدد النوع الآن، بل نتركه ليتم تحديده عند "استخدام" الدالة فعلياً.
تخيل الموقف التالي: تريد إنشاء دالة تأخذ قيمة وتعيدها كما هي (Identity Function).
- إذا جعلتها تستقبل
numberستعمل مع الأرقام فقط. - إذا جعلتها تستقبل
stringستعمل مع النصوص فقط. - إذا استخدمت نوع
anyستفقد TypeScript قدرتها على معرفة نوع البيانات العائدة، وهذا يقلل من أمان الكود.
الحل هو الـ Generics! فهي تسمح للدالة بأن تقول: "أنا سأتعامل مع نوع سأعرفه لاحقاً عند استدعائك لي". 🎯
🛠️ كيف نكتب الـ Generics؟ (الصيغة الأساسية)
لتعريف Generic، نستخدم "وسم" (Type Parameter) يكون عادةً عبارة عن حرف كبير مثل <T>. حرف T هو اختصار لكلمة Type.
إليك كيف نحول دالة بسيطة إلى Generic Function:
// T هنا يمثل "نوعاً مجهولاً" سيتم تحديده عند استدعاء الدالة
function identity<T>(arg: T): T {
return arg; // تعيد القيمة بنفس النوع الذي استلمته
}
// الاستخدام الأول: تمرير نوع number
let result1 = identity<number>(10);
// الآن TypeScript تعرف أن result1 هو number
// الاستخدام الثاني: تمرير نوع string
let result2 = identity<string>("Hello Codex");
// الآن TypeScript تعرف أن result2 هو string
شرح الكود:
<T>: تخبر TypeScript أن هذه الدالة هي Generic.(arg: T): تعني أن الوسيطargسيكون من النوعT.: T: تعني أن القيمة العائدة من الدالة ستكون أيضاً من النوعT.
💡 هل يجب دائماً كتابة النوع بين < > عند الاستدعاء؟
سؤال ذكي! الإجابة هي: لا. TypeScript ذكية جداً وتستطيع القيام بما يسمى Type Inference (استنتاج النوع). هي تنظر إلى القيمة التي مررتها وتستنتج النوع تلقائياً.
انظر إلى هذا المثال المبسط:
// لا حاجة لكتابة <string> هنا، TypeScript ستعرفها تلقائياً
let result = identity("Welcome to Codex Academy");
// TypeScript استنتجت أن T هي string بناءً على القيمة الممررة
📦 استخدام Generics مع المصفوفات (Arrays)
أكثر استخدام شائع للـ Generics هو التعامل مع المصفوفات، لأن المصفوفة بطبيعتها يمكن أن تحتوي على أي نوع من البيانات.
لنقم بإنشاء دالة تأخذ مصفوفة وتعيد العنصر الأول منها:
function getFirstElement<T>(array: T[]): T {
return array[0]; // تعيد أول عنصر من النوع T
}
// مثال مع مصفوفة أرقام
const numbersArray = [10, 20, 30];
const firstNum = getFirstElement(numbersArray); // النوع هنا number
// مثال مع مصفوفة نصوص
const namesArray = ["Ahmed", "Sara", "Ali"];
const firstName = getFirstElement(namesArray); // النوع هنا string
في هذا المثال، استخدمنا T[] لتعريف مصفوفة من النوع T. هذا يجعل الدالة مرنة جداً؛ فهي تعمل مع أي نوع مصفوفة تمنحها إياها. 🌟
🏫 استخدام Generics مع الكلاسات (Classes)
لا تقتصر الـ Generics على الدوال فقط، بل يمكنك استخدامها في الكلاسات أيضاً لإنشاء حاويات بيانات مرنة.
class Box<T> {
content: T;
constructor(value: T) {
this.content = value;
}
getContent(): T {
return this.content;
}
}
// إنشاء صندوق يحتوي على رقم
const numberBox = new Box<number>(100);
console.log(numberBox.getContent()); // 100
// إنشاء صندوق يحتوي على نص
const stringBox = new Box<string>("TypeScript is Awesome");
console.log(stringBox.getContent()); // TypeScript is Awesome
في هذا المثال، الكلاس Box لا يهتم بماذا سيخزن، هو فقط يضمن أن النوع الذي تضعه عند إنشاء الكائن هو نفسه النوع الذي ستسترجعه عند استدعاء getContent.
📝 ملخص سريع لما تعلمناه:
- الـ Generics تسمح لنا بكتابة كود مرن يعمل مع أنواع مختلفة دون فقدان ميزة فحص الأنواع.
- نستخدم
<T>لتعريف النوع المجهول. - يمكن لـ TypeScript استنتاج النوع تلقائياً (Type Inference).
- يمكن استخدام الـ Generics في الدوال، المصفوفات، والكلاسات.
🎓 اختبر معلوماتك
التعليقات
شاركنا رأيك أو أسئلتك حول هذا المقال