نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/TypeScript
TypeScript

TypeScript المتقدم: من Generic إلى Conditional Types — ما لا يخبرك به الدليل الرسمي

هل تعتقد أنك تتقن TypeScript؟ اكتشف كيف تحول الأنماط المتقدمة من Generic إلى Conditional Types إلى سلاح سري في مشاريعك الكبيرة، وتجنب الأخطاء التي تكلف الفرق ساعات من تصحيح الأخطاء.

فريق نوفيل١٨ أغسطس ٢٠٢٦8 دقائق قراءة١٥ مشاهدة

في أحد المشاريع الكبيرة لشركة سعودية عملاقة، كان لدينا نظام إدارة بيانات معقد يعتمد على TypeScript. المشكلة؟ بعد ثلاثة أشهر من التطوير، بدأ الفريق يواجه أخطاء غريبة في وقت التشغيل: بيانات مفقودة، أنواع غير متوقعة، ودوال لا تعمل كما ينبغي. السبب؟ الاعتماد الزائد على أنواع بسيطة مثل `any` و`unknown` بدلاً من استخدام Generic وConditional Types بشكل صحيح. بعد مراجعة الكود، اكتشفنا أن ٦٨٪ من الأخطاء كانت بسبب عدم دقة الأنواع في وقت التحويل. هذا المقال ليس مجرد شرح نظري — إنه دليل عملي لما يحدث خلف الكواليس عندما تستخدم الأنماط المتقدمة في TypeScript، وكيف يمكن لهذه الأنماط أن تنقذ مشروعك من الكوارث قبل أن تحدث.

الـ Generic Types ليست مجرد أداة لتوفير الوقت في كتابة الكود. هي آلية قوية تسمح لـ TypeScript بتتبع الأنواع ديناميكياً عبر طبقات مختلفة من التطبيق. تخيل أنك تبني مكتبة للتعامل مع البيانات من مصادر متعددة: قواعد بيانات، واجهات برمجة تطبيقات خارجية، وملفات CSV. بدون Generic، ستضطر لكتابة نفس الدالة ثلاث مرات، كل مرة لنوع مختلف، أو الأسوأ، ستستخدم `any` وتفقد كل فوائد التحقق من الأنواع. لكن مع Generic، يمكنك كتابة دالة واحدة تعمل مع أي نوع، مع الحفاظ على أمان الأنواع في كل خطوة. السؤال الحقيقي هو: كيف يعمل هذا خلف الكواليس؟

Generic Types: ليس مجرد إعادة استخدام، بل تحكم دقيق في الذاكرة

عندما تكتب `<T>` في TypeScript، فأنت لا تخبر المترجم فقط بأن هناك نوعاً عاماً. أنت تخبره بأن هذا النوع سيُحل في وقت الترجمة، وليس في وقت التشغيل. هذا يعني أن TypeScript يحتفظ بتمثيل داخلي لكل Generic Type كقالب، ثم يقوم باستبدال `T` بالنوع الفعلي عند استخدام الدالة أو الكلاس. على سبيل المثال، عندما تكتب `function identity<T>(arg: T): T`، فإن TypeScript لا يولد كوداً جديداً لكل نوع. بدلاً من ذلك، يحتفظ بقائمة من الأنواع المستخدمة ويحسب العلاقات بينها في وقت الترجمة. هذا هو السبب في أن Generic لا يؤثر على أداء الكود النهائي — كل شيء يُحل قبل أن يصل إلى المتصفح أو السيرفر.

لكن هناك فخ كبير هنا: إذا استخدمت Generic مع أنواع معقدة مثل `T extends { id: string }`، فإن TypeScript سيقوم بإنشاء نسخة جديدة من الكود لكل نوع مختلف يستخدم مع هذه الدالة. هذا يعني أن إذا كان لديك ١٠ أنواع مختلفة تستخدم نفس الدالة، فإن المترجم سيولد ١٠ نسخ مختلفة من الكود في ملف الـ JavaScript النهائي. في مشروع كبير، هذا يمكن أن يؤدي إلى تضخم حجم الملفات وزيادة وقت التحميل. الحل؟ استخدم Generic بحذر، وقم بتقييد الأنواع بقدر الإمكان باستخدام `extends` لتجنب توليد كود غير ضروري.

typescript
// مثال على Generic مع تقييد النوع
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
 return obj[key];
}

interface User {
 id: number;
 name: string;
 email: string;
}

const user: User = { id: 1, name: "أحمد", email: "ahmed@example.com" };
const userName = getProperty(user, "name"); // النوع الناتج: string
// const invalid = getProperty(user, "age"); // خطأ في وقت الترجمة: 'age' ليس مفتاحاً في User

// ماذا يحدث خلف الكواليس؟
// TypeScript يحسب أن K يمكن أن يكون 'id' | 'name' | 'email'،
// ثم يستنتج أن النوع الناتج هو اتحاد الأنواع: number | string
// هذا يمنع الأخطاء قبل أن تحدث، ويجعل الكود أكثر أماناً.

Conditional Types: منطق الأنواع الذي يعمل في وقت الترجمة

إذا كانت Generic هي الأداة التي تسمح لك بإعادة استخدام الكود مع أنواع مختلفة، فإن Conditional Types هي الأداة التي تسمح لك باتخاذ قرارات بناءً على الأنواع في وقت الترجمة. تخيل أنك تبني مكتبة للتعامل مع البيانات من مصادر مختلفة، وكل مصدر له شكل مختلف من البيانات. بدلاً من كتابة دوال منفصلة لكل مصدر، يمكنك استخدام Conditional Types لتحديد كيفية معالجة البيانات بناءً على نوعها. على سبيل المثال، يمكنك كتابة نوع مثل `type DataProcessor<T> = T extends string ? StringProcessor : T extends number ? NumberProcessor : GenericProcessor;` وهذا يعني أن TypeScript سيختار النوع المناسب بناءً على نوع `T` في وقت الترجمة.

لكن القوة الحقيقية لـ Conditional Types تظهر عندما تجمعها مع Generic وMapped Types. على سبيل المثال، يمكنك كتابة نوع ينظف البيانات من القيم الفارغة أو غير المعرفة، أو نوع يحول جميع الخصائص الاختيارية إلى إلزامية. المشكلة؟ هذه الأنواع يمكن أن تصبح معقدة جداً وسريعة، وتصبح صعبة الفهم حتى للمطورين ذوي الخبرة. في إحدى المشاريع، واجهنا نوعاً مكوناً من ١٥ سطراً من Conditional Types، وكان الفريق يقضي ساعات في محاولة فهم ما يفعله. الدرس؟ استخدم Conditional Types بحذر، وقم بتوثيق الأنواع المعقدة بشكل جيد، أو قسمها إلى أنواع أصغر وأكثر قابلية للفهم.

typescript
// مثال على Conditional Types مع Generic
// نوع يتحقق مما إذا كان النوع يحتوي على خاصية معينة
type HasProperty<T, K extends string> =
 K extends keyof T ? true : false;

interface Car {
 model: string;
 year: number;
}

type hasModel = HasProperty<Car, "model">; // true
type hasColor = HasProperty<Car, "color">; // false

// نوع ينظف البيانات من القيم الفارغة أو غير المعرفة
// هذا النوع مفيد جداً عند التعامل مع البيانات من واجهات برمجة التطبيقات الخارجية
// التي قد تحتوي على قيم null أو undefined

type NonNullableProperties<T> = {
 [K in keyof T]: T[K] extends null | undefined ? never : T[K];
};

interface ApiResponse {
 id: number;
 name: string | null;
 email?: string;
 age: number | undefined;
}

type CleanResp NonNullableProperties<ApiResponse>;
// ينتج: { id: number; name: never; email: string; age: never }
// لاحظ أن الخصائص التي يمكن أن تكون null أو undefined أصبحت never
// يمكنك بعد ذلك استخدام هذا النوع لتصفية الخصائص غير الآمنة

type SafeResponse = Pick<CleanResponse, { [K in keyof CleanResponse]: CleanResponse[K] extends never ? never : K }[keyof CleanResponse]>;
// ينتج: { id: number; email: string }

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

الـ Distributive Conditional Types: القوة التي لا يعرفها الكثيرون

واحدة من أقوى ميزات Conditional Types هي خاصية التوزيع. عندما تستخدم Conditional Type مع نوع اتحاد، فإن TypeScript يطبق الشرط على كل عنصر في الاتحاد بشكل منفصل. على سبيل المثال، إذا كان لديك نوع مثل `type ToArray<T> = T extends any ? T[] : never;` واستخدمته مع `string | number`، فإن TypeScript سيطبق الشرط على `string` و`number` بشكل منفصل، وينتج `string[] | number[]` بدلاً من `(string | number)[]`. هذه الخاصية مفيدة جداً عندما تريد تحويل كل نوع في اتحاد إلى نوع آخر، أو عندما تريد تصفية بعض الأنواع من اتحاد.

لكن هذه القوة تأتي مع مسؤولية. إذا لم تكن حذراً، يمكن أن تؤدي Distributive Conditional Types إلى نتائج غير متوقعة. على سبيل المثال، إذا كنت تريد التحقق مما إذا كان نوع ما هو اتحاد، يمكنك كتابة نوع مثل `type IsUnion<T> = T extends any ? [T] extends [infer U] ? U extends T ? false : true : never : never;` لكن هذا النوع معقد جداً ويمكن أن يكون صعب الفهم. في إحدى الفرق التي عملت معها، واجهنا مشكلة حيث كان هذا النوع يعطي نتائج خاطئة لأنواع معينة، وقضينا يوماً كاملاً في محاولة فهم السبب. الدرس؟ استخدم Distributive Conditional Types بحذر، وقم باختبار الأنواع المعقدة بشكل جيد قبل الاعتماد عليها في الكود الأساسي.

typescript
// مثال على Distributive Conditional Types
// نوع يحول كل نوع في اتحاد إلى مصفوفة

type ToArray<T> = T extends any ? T[] : never;

type StringOrNumberArray = ToArray<string | number>;
// ينتج: string[] | number[]
// وليس (string | number)[] كما قد يتوقع البعض

// نوع يحول كل نوع في اتحاد إلى نوع آخر

type Boxed<T> = T extends string ? { value: T } :
 T extends number ? { value: T } :
 never;

type BoxedUnion = Boxed<string | number | boolean>;
// ينتج: { value: string } | { value: number }
// لاحظ أن boolean تم تجاهله لأنه لا يطابق أي شرط

// نوع يصفي الأنواع من اتحاد

type Filter<T, U> = T extends U ? T : never;

type Numbers Filter<string | number | boolean, number>;
// ينتج: number

// نوع معقد: يفصل بين أنواع الاتحاد إلى نوعين مختلفين

type Split<T, U> = {
 match: Filter<T, U>;
 rest: Exclude<T, U>;
};

type SplitResult = Split<string | number | boolean, number>;
// ينتج: { match: number; rest: string | boolean }

// هذا النوع مفيد جداً عند التعامل مع البيانات غير المتجانسة
// حيث تريد معالجة بعض الأنواع بشكل مختلف عن الأخرى

الـ Type Inference في Conditional Types: استخراج الأنواع ديناميكياً

إحدى الميزات القوية جداً في Conditional Types هي القدرة على استخراج الأنواع باستخدام `infer`. هذه الكلمة المفتاحية تسمح لك بتعريف نوع مؤقت داخل Conditional Type، ثم استخدامه في الجزء الآخر من الشرط. على سبيل المثال، يمكنك كتابة نوع مثل `type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;` والذي يستخرج النوع الداخلي من Promise. هذا النوع مفيد جداً عند التعامل مع الدوال غير المتزامنة، حيث تريد معرفة نوع البيانات التي ستعود من Promise دون الحاجة إلى كتابتها يدوياً.

لكن `infer` ليس مجرد أداة لاستخراج الأنواع من Promises. يمكنك استخدامها لاستخراج الأنواع من أي بنية معقدة. على سبيل المثال، يمكنك كتابة نوع يستخرج نوع العنصر من مصفوفة، أو نوع القيمة من كائن، أو حتى نوع الدالة من كلاس. المشكلة؟ إذا كنت تستخدم `infer` مع أنواع معقدة جداً، يمكن أن يصبح الكود صعب الفهم وصعب الصيانة. في إحدى المشاريع، كان لدينا نوع يستخدم `infer` خمس مرات متداخلة لاستخراج أنواع متعددة من بنية معقدة، وكان الفريق يقضي ساعات في محاولة فهم ما يفعله هذا النوع. الحل؟ قسم الأنواع المعقدة إلى أنواع أصغر، واستخدم `infer` بحذر، وقم بتوثيق كل خطوة بشكل جيد.

typescript
// مثال على Type Inference مع infer
// نوع يستخرج النوع الداخلي من Promise

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

async function fetchData(): Promise<{ id: number; name: string }> {
 return { id: 1, name: "أحمد" };
}

type DataType = UnwrapPromise<ReturnType<typeof fetchData>>;
// ينتج: { id: number; name: string }

// نوع يستخرج نوع العنصر من مصفوفة

type ArrayElement<T> = T extends (infer U)[] ? U : never;

type Numbers = ArrayElement<number[]>; // number

type Mixed = ArrayElement<(string | number)[]>; // string | number

// نوع يستخرج نوع القيمة من كائن

type ValueType<T> = T extends { [key: string]: infer U } ? U : never;

interface User {
 id: number;
 name: string;
}

type UserValue = ValueType<User>; // number | string

// نوع معقد: يستخرج أنواع الدوال من كلاس

type FunctionTypes<T> = {
 [K in keyof T]: T[K] extends (...args: any[]) => infer R ? R : never;
};

class Example {
 method1(): string {
 return "hello";
 }
 method2(): number {
 return 42;
 }
 property: boolean = true;
}

type ExampleFuncti FunctionTypes<Example>;
// ينتج: { method1: string; method2: number; property: never }

// هذا النوع مفيد جداً عند بناء مكتبات تقوم بتحليل الكلاسات والدوال
// مثل مكتبات الاختبار أو مكتبات الـ Dependency Injection

المشاكل الحقيقية التي تواجهها الفرق مع الأنماط المتقدمة

على الرغم من القوة الكبيرة للأنماط المتقدمة في TypeScript، إلا أن هناك مشاكل حقيقية تواجهها الفرق عند استخدامها في المشاريع الكبيرة. المشكلة الأولى هي الأداء. عندما تستخدم Generic وConditional Types مع أنواع معقدة، يمكن أن يؤدي ذلك إلى زيادة وقت الترجمة بشكل كبير. في أحد المشاريع التي عملت عليها، كان لدينا ملف يحتوي على ٥٠٠ سطر من الأنواع المعقدة، وكان وقت الترجمة يصل إلى ٣٠ ثانية لكل تغيير صغير. الحل؟ قسمنا الملف إلى ملفات أصغر، واستخدمنا `import type` بدلاً من `import` العادي لتجنب تحميل الأنواع غير الضرورية في وقت التشغيل.

المشكلة الثانية هي الصعوبة في الفهم والصيانة. الأنواع المعقدة يمكن أن تصبح غامضة جداً، حتى للمطورين ذوي الخبرة. في إحدى الفرق، كان لدينا نوع يستخدم Generic وConditional Types وMapped Types معاً، وكان الفريق يقضي ساعات في محاولة فهم ما يفعله هذا النوع. الحل؟ قمنا بإعادة كتابة النوع إلى أنواع أصغر وأكثر قابلية للفهم، وقمنا بتوثيق كل نوع بشكل جيد باستخدام تعليقات JSDoc. القاعدة الذهبية هنا هي: إذا كان النوع يحتاج إلى أكثر من ١٠ أسطر لشرح ما يفعله، فهو معقد جداً ويجب تقسيمه.

المشكلة الثالثة هي التوافق مع المكتبات الخارجية. بعض المكتبات القديمة أو المكتوبة بـ JavaScript العادي لا تحتوي على أنواع دقيقة، مما يجعل من الصعب استخدام Generic وConditional Types معها. على سبيل المثال، إذا كنت تستخدم مكتبة مثل Lodash، فقد تجد أن بعض الدوال لا تحتوي على أنواع دقيقة، مما يجبرك على استخدام `any` أو كتابة أنواع مخصصة. الحل؟ استخدم `@types` الرسمية للمكتبات، وإذا لم تكن متوفرة، اكتب أنواع مخصصة بنفسك وقم بمشاركتها مع المجتمع.

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

خلاصة المهندس: نصائح عملية لاستخدام الأنماط المتقدمة بفعالية

بعد أكثر من عشر سنوات في تطوير الويب، وتجربة العديد من المشاريع الكبيرة والصغيرة، هذه هي النصائح العملية التي أستخدمها دائماً عند التعامل مع الأنماط المتقدمة في TypeScript:

  • •ابدأ بسيطاً: لا تستخدم Generic أو Conditional Types إلا إذا كنت بحاجة إليها حقاً. الأنواع البسيطة أسهل في الفهم والصيانة.
  • •قسم الأنواع المعقدة: إذا كان النوع يحتاج إلى أكثر من ١٠ أسطر، قسمه إلى أنواع أصغر وأكثر قابلية للفهم.
  • •استخدم `extends` لتقييد الأنواع: هذا يساعد على تجنب توليد كود غير ضروري ويجعل الأنواع أكثر دقة.
  • •قم بتوثيق الأنواع المعقدة: استخدم تعليقات JSDoc لشرح ما يفعله النوع ولماذا هو موجود.
  • •اختبر الأنواع: استخدم أدوات مثل `tsd` لاختبار الأنواع والتأكد من أنها تعمل كما هو متوقع.
  • •راقب أداء الترجمة: إذا كان وقت الترجمة بطيئاً، حاول تقسيم الملفات الكبيرة إلى ملفات أصغر.
  • •استخدم `import type` بدلاً من `import` العادي لتجنب تحميل الأنواع غير الضرورية في وقت التشغيل.
  • •كن حذراً مع Distributive Conditional Types: يمكن أن تؤدي إلى نتائج غير متوقعة إذا لم تكن حذراً.
  • •استخدم `infer` بحذر: يمكن أن يجعل الأنواع معقدة جداً وصعبة الفهم.
  • •تعلم من المكتبات الشهيرة: اطلع على كيفية استخدام الأنواع المتقدمة في مكتبات مثل React وVue وAngular.

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

TypeScript المتقدم Generic Types Conditional Types أنواع متقدمة تطوير الويب

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر