Generics في TypeScript: لماذا هي ضرورية وكيفية استخدامها للمبتدئين

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

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

ابدأ الدورة

مقدمة: ما هي Generics ولماذا هي مهمة؟

\n\n

عند تعلم TypeScript، سرعان ما تواجه موقفًا تحتاج فيه إلى كتابة دالة أو كلاس يمكنه العمل مع أنواع بيانات مختلفة. بدون أدوات خاصة، سيكون عليك إما تكرار الكود لكل نوع، أو استخدام any، مما يقتل سحر التصنيف الصارم. هنا تأتي Generics (الجينيريكس أو التعميمات) للإنقاذ.

\n\n

Generics هي آلية قوية في TypeScript تسمح لك بإنشاء مكونات (دوال، كلاسات، واجهات) تعمل مع أنواع مختلفة مع الحفاظ على التصنيف الصارم. تخيل أنك تكتب حاوية عالمية يمكنها تخزين التفاح والكتب والأرقام، ولكن TypeScript يعرف بالضبط ما بداخلها في كل حالة محددة.

\n\n

في هذه المقالة، سنشرح لماذا نحتاج إلى Generics، وكيفية تعريفها واستخدامها في المشاريع الحقيقية. ستتعلم كيفية جعل كودك أكثر مرونة وأمانًا وقابلية لإعادة الاستخدام.

\n\n

أساسيات Generics: الصياغة وأول دالة

\n\n

لنبدأ بمثال بسيط. لنفترض أننا نحتاج إلى دالة تأخذ وسيطًا وتعيده كما هو (دالة الهوية). بدون الجينيريكس، سنكتب هكذا:

\n\n
function identity(arg: number): number {\n    return arg;\n}
\n\n

لكن هذه الدالة تعمل فقط مع الأرقام. لجعلها عامة، يمكننا استخدام any:

\n\n
function identity(arg: any): any {\n    return arg;\n}
\n\n

المشكلة هي أننا نفقد معلومات النوع. TypeScript لا يعرف أنه سيعيد نفس النوع الذي تم تمريره. هنا تأتي Generics للإنقاذ:

\n\n
function identity<T>(arg: T): T {\n    return arg;\n}\n\n// الاستخدام:\nlet output1 = identity<string>("مرحبا"); // النوع string\nlet output2 = identity<number>(42);       // النوع number
\n\n

دعنا نحلل الصياغة:

\n
    \n
  • <T> — هو معامل النوع. يمكنك استخدام أي حرف، ولكن حسب الاتفاقية الأكثر شيوعًا استخدام T (Type)، U، V.
  • \n
  • arg: T — الوسيط له النوع T.
  • \n
  • : T بعد الأقواس — الدالة تعيد قيمة من النوع T.
  • \n
\n\n

الآن TypeScript يعرف: إذا قمت بتمرير نص، فسيكون الناتج نصًا. إذا كان رقمًا، فسيكون رقمًا. استنتاج الأنواع يعمل تلقائيًا في كثير من الأحيان، لذا يمكنك كتابة identity("مرحبا") فقط — سيفهم TypeScript أن T هو string.

\n\n

Generics في الدوال: العمل مع المصفوفات والقيود

\n\n

غالبًا ما تُستخدم الجينيريكس للعمل مع المصفوفات أو المجموعات. لننظر إلى دالة تعيد العنصر الأول من المصفوفة:

\n\n
function getFirst<T>(arr: T[]): T | undefined {\n    return arr[0];\n}\n\nconst numbers = [1, 2, 3];\nconst firstNumber = getFirst(numbers); // النوع number\n\nconst strings = ["a", "b", "c"];\nconst firstString = getFirst(strings); // النوع string
\n\n

لاحظ: T[] هي كتابة مختصرة لـ Array<T>. الدالة تعمل مع أي مصفوفة، ولكنها تعيد عنصرًا من نفس النوع المخزن في المصفوفة.

\n\n

القيود (Constraints) باستخدام extends

\n\n

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

\n\n
interface HasLength {\n    length: number;\n}\n\nfunction getLength<T extends HasLength>(arg: T): number {\n    return arg.length;\n}\n\ngetLength("نص");     // صحيح، النص له length\ngetLength([1, 2, 3]);    // صحيح، المصفوفة لها length\ngetLength(42);           // خطأ! الرقم ليس له length

المدونات

توصيات الكتب