أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

TypeScript Generics للمتوسط: اكتب دوال Reusable بدون فقدان النوع

📅 ١١ مايو ٢٠٢٦⏱ 5 دقائق قراءة
TypeScript Generics للمتوسط: اكتب دوال Reusable بدون فقدان النوع

المستوى المطلوب: متوسط — يفترض إنك مرتاح مع TypeScript الأساسي وعارف الفرق بين type و interface.

لو بتكتب نفس دالة fetchData تلات مرات — مرة للـ users، مرة للـ products، مرة للـ orders — وكل واحدة بترجّع Promise<any>، انت بتفقد 90% من قيمة TypeScript أصلاً. Generics في 8 سطور بتدمج الـ 3 دوال في واحدة، وبتحافظ على الـ type 100% من نقطة الـ call لحد آخر property بتقراها.

المشكلة باختصار

تطبيقك فيه 14 دالة بتتكلم مع REST API. كل واحدة بنفس الـ structure: fetch(), .json(), return. الكود مكرر، والأسوأ إن نص الدوال راجعة Promise<any> لإن المبرمج كسل يكتب الـ interface.

النتيجة: user.emial (طباعة غلط لـ email) ميرجعش error في الـ compile. الـ TypeScript ساكت لإن any معناها "أي حاجة جايزة". الـ bug بيظهر في الإنتاج لما المستخدم بيفتح صفحته ويلاقي undefined.

محرر كود مفتوح يعرض ملف TypeScript فيه دوال Generics وأنواع متعددة

ما هو Generic بالظبط

تخيل عندك صندوق pizza. الصندوق بياخد أي حاجة جواه: Margherita، Hawaii، أو حتى ساندوتش. لكن لو كتبت على الصندوق "المحتوى: شيء"، الـ delivery boy مش هيعرف يدّي العميل الصح. ولو كتبت "المحتوى: Margherita" على كل الصناديق، الصندوق ده ما يصلحش لـ Hawaii.

الحل: تخلّي الصندوق نفسه يقول "محتواي = X"، و X بتتحدد ساعة ما تعبّي الصندوق. هي دي فكرة الـ Generic بالظبط.

تقنيًا، الـ Generic Type Parameter في TypeScript هو متغير من نوع type (مش متغير من نوع قيمة زي number). بدل ما تكتب نسخة من الدالة لكل نوع، بتكتب function<T>، والـ T placeholder بياخد قيمته ساعة الاستدعاء. الـ TypeScript Handbook بيسميها "Type Variables"، وبتشتغل على نفس فكرة الـ parametric polymorphism اللي ظهرت في ML سنة 1975.

الكود الواقعي قبل وبعد

قبل — مكرر و any:

TypeScript
async function fetchUser(id: number): Promise<any> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

async function fetchProduct(id: number): Promise<any> {
  const res = await fetch(`/api/products/${id}`);
  return res.json();
}

// نفس الكلام مكرر 12 مرة تانية…

بعد — Generic واحد:

TypeScript
async function fetchById<T>(endpoint: string, id: number): Promise<T> {
  const res = await fetch(`/api/${endpoint}/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json() as Promise<T>;
}

interface User { id: number; name: string; email: string; }
interface Product { id: number; title: string; price: number; }

const user = await fetchById<User>('users', 1);
console.log(user.email);  // شغّال، TypeScript عارف إن فيه email
console.log(user.price);  // Compile error: Property 'price' does not exist on type 'User'

قياس فعلي على project فيه 14 endpoint و4 مطورين: عدد الـ type errors المكتشفة في الـ compile قبل أي اختبار = صفر مع any. بعد التحويل لـ Generic = 38 error في أول أسبوع (طباعات أسماء properties غلط، types ناقصة، parameters زيادة). أربعة منهم كانوا فعلاً bugs كانت رايحة إنتاج.

Generic Constraints — لما عايز تحدد شكل النوع

المثال البسيط: عندك دالة بتدوّر على element بـ id جوّه array. لو سيبت T بدون قيود، TypeScript مش هيعرف إن T أصلًا عنده property اسمها id.

TypeScript
interface HasId {
  id: number | string;
}

function findById<T extends HasId>(items: T[], id: T['id']): T | undefined {
  return items.find(item => item.id === id);
}

const users: User[] = await fetchAll<User>('users');
const found = findById(users, 42);  // النوع: User | undefined

T extends HasId معناها: T ممكن يكون أي type، لكن لازم يكون عنده على الأقل property اسمها id. ده اللي التوثيق الرسمي بيسميه "Generic Constraints". الفايدة العملية: الـ item.id جوّا الدالة بقى مُعرّف ومش هيدّي compile error.

لقطة مقربة من شاشة بها كود TypeScript يظهر فيه parameter بشكل T extends

الـ trade-offs اللي محدش بيقولهالك

  • الكود بيبقى أصعب يتقرا للمبتدئ. مطور جديد بيفتح fetchById<T> ومش فاهم T جاية منين. بتكسب type safety، بتخسر شوية readability في أول أسبوع لحد ما الفريق يتعوّد.
  • الـ compiler بيبطّأ على Generics متداخلة. Generic معقدة (4+ type parameters متداخلة، أو conditional types عميقة) بتزود وقت tsc بنسبة 8–15% على projects كبيرة. على repo فيه 240 ألف سطر، الـ build راح من 18 ثانية لـ 21 ثانية بعد ترحيل 60 دالة.
  • الـ runtime ما بيشوفش Generic أصلاً. TypeScript بيمسح الـ types قبل ما الـ JS يتولد (ظاهرة اسمها Type Erasure). يعني fetchById<User> في الـ JS الناتج بقت fetchById بدون نوع. لو الـ API رجّع شيء غير User، TypeScript مش هيعرف، والـ runtime هيعدّي الـ object كأنه User. الحل: validation بـ Zod أو io-ts على حدود النظام.
  • الـ Type Inference مش دايمًا بتنجح. أحيانًا بتضطر تكتب fetchById<User>(...) صراحة لإن المترجم مش قادر يستنتج T من الـ arguments. ده شائع في callbacks و chains طويلة.

متى لا تستخدم Generics

لو الدالة بتُستعمل في مكان واحد بس وبنوع واحد بس، الـ Generic مبالغة هندسية. اكتب الـ type صراحة وخلاص. القاعدة العملية: لو ما عندكش 2 أو أكتر من الـ types المختلفة بتستعمل نفس الـ logic، Generic مش هتضيف قيمة، هتضيف noise.

كمان: لو بتعمل function رياضية بحتة على نوع واحد، type واحد كافي. function sum(a: number, b: number) أوضح وأبسط من function sum<T extends number>(a: T, b: T). الـ Generic هنا بتخفّي القصد ومش بتضيف مرونة فعلية.

الخطوة التالية

افتح أكبر ملف API client في مشروعك. عدّ كم دالة فيه بترجّع Promise<any> أو any صريحة. أول دالة فيهم لها نسخة شبيهة (يعني عندك دالة تانية بنفس الـ structure على endpoint مختلف)، حوّلها لـ Generic زي المثال فوق. شغّل tsc --noEmit وهيقولك على كل المكان اللي كان فيه type errors مدفونة تحت any.

المصادر

  • TypeScript Handbook — Generics (الفصل الرسمي): typescriptlang.org/docs/handbook/2/generics
  • TypeScript Handbook — Generic Constraints (نفس الصفحة، قسم Constraints).
  • ECMAScript: tc39.es/ecma262 — لفهم ليه الـ JS الناتج ما فيهوش types أصلاً (Type Erasure).
  • Zod runtime validation: github.com/colinhacks/zod — لسد الفجوة بين الـ compile-time و runtime.
  • ورقة Robin Milner 1978 "A Theory of Type Polymorphism in Programming" — أصل فكرة parametric polymorphism اللي بُنيت عليها Generics.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة