أدوات TypeScript Generics للاستخدام اليومي: دليل عملي

Frontend2026-10-04TryQuickToolBox

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

ما هي Generics ولماذا يجب أن تهتم؟

Generics هي طريقة لجعل الأنواع معاملات (parameterize). بدلاً من تثبيت نوع مثل string أو number، تستخدم متغير نوع (غالبًا T) يتم استبداله بنوع محدد عند استخدام الدالة أو الفئة.

لماذا هذا مهم؟ لأنه يتيح لك كتابة كود قابل لإعادة الاستخدام يظل آمنًا من حيث النوع. بدون generics، إما أن تكرر الكود أو تستخدم any، مما يبطل الغرض من TypeScript.

الدوال Generic: حجر الأساس

لنبدأ بمثال بسيط. لنفترض أنك تريد دالة تُرجع العنصر الأول من مصفوفة.

function firstElement(arr: any[]): any {
  return arr[0];
}

هذا يعمل، لكنك تفقد معلومات النوع. إذا مررت مصفوفة من السلاسل النصية، فإن نوع الإرجاع هو any، وليس string. الآن مع generics:

function firstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

الآن يعرف TypeScript أنه إذا مررت string[]، فستحصل على string | undefined كإرجاع. يتم الحفاظ على النوع.

يمكنك أيضًا استخدام معاملات نوع متعددة:

function pair<A, B>(a: A, b: B): [A, B] {
  return [a, b];
}

الواجهات والأسماء المستعارة للنوع Generic

لا تقتصر generics على الدوال. يمكنك إنشاء واجهات generic وأسماء مستعارة للنوع لوصف هياكل بيانات مرنة.

interface Box<T> {
  value: T;
}

const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 42 };

هذا مفيد بشكل خاص لاستجابات API. بدلاً من تعريف واجهة جديدة لكل نقطة نهاية (endpoint)، يمكنك تعريف ApiResponse<T> generic:

interface ApiResponse<T> {
  data: T;
  status: number;
}

type User = { id: number; name: string };

const response: ApiResponse<User> = {
  data: { id: 1, name: "Alice" },
  status: 200,
};

قيود Generic: إضافة قواعد

أحيانًا تريد تقييد الأنواع التي يمكن استخدامها مع generic الخاص بك. هنا تأتي القيود. استخدم الكلمة المفتاحية extends لطلب أن يكون للنوع خصائص معينة.

function getLength<T extends { length: number }>(item: T): number {
  return item.length;
}

getLength("hello"); // OK
getLength([1, 2, 3]); // OK
getLength(42); // Error: number doesn't have length

تجعل القيود generics أكثر قابلية للتنبؤ وتمنع أخطاء وقت التشغيل.

الفئات Generic

يمكن أن تكون الفئات أيضًا generic. هذا مفيد لهياكل البيانات مثل المكدسات (stacks) أو الطوابير (queues) أو المستودعات (repositories).

class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
const top = numberStack.pop(); // number | undefined

متى تستخدم Generics (ومتى لا تستخدمها)

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

استخدم Generics عندما تجنب Generics عندما
تحتاج إلى الحفاظ على علاقات النوع يكفي نوع اتحاد بسيط أو any
تكتب أدوات قابلة لإعادة الاستخدام النوع دائمًا هو نفسه
تريد تجنب تكرار الكود تجعل الكود أصعب في الفهم

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

الأنماط الشائعة وأفضل الممارسات

مثال من العالم الحقيقي: عميل API Generic

لنربط كل شيء بمثال عملي: عميل API بسيط يجلب البيانات ويعيد استجابة مكتوبة النوع.

async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  return response.json() as Promise<T>;
}

interface Post {
  userId: number;
  id: number;
  title: string;
  body: string;
}

async function main() {
  const post = await fetchData<Post>("https://jsonplaceholder.typicode.com/posts/1");
  console.log(post.title); // string
}

هذا النمط شائع في كود الواجهة الأمامية. تحصل على أمان النوع دون تكرار نفسك لكل نقطة نهاية.

الأسئلة الشائعة

ماذا يعني صيغة <T> في TypeScript؟

<T> تعلن عن معامل نوع. إنها عنصر نائب لنوع سيتم تحديده عند استخدام الدالة أو الواجهة أو الفئة. يمكنك تسميتها أي شيء، لكن T هو التقليد لنوع واحد.

هل يمكنني استخدام معاملات نوع generic متعددة؟

نعم. يمكنك تعريف ما تحتاجه، مفصولة بفواصل: <T, U, V>. استخدم أسماء وصفية للحفاظ على قابلية قراءة الكود.

كيف أقيد generic لقبول الكائنات فقط؟

استخدم T extends object. هذا يضمن أن T هو نوع كائن وليس نوعًا بدائيًا مثل string أو number. يمكنك أيضًا تقييده بأشكال محددة، مثل T extends { id: number }.

Generics جزء أساسي من كتابة TypeScript قابل للصيانة. إذا كنت تعمل مع بيانات JSON وتريد التحقق منها أو تنسيقها بسرعة، جرب JSON Formatter الخاص بنا لطباعة استجابات API الخاصة بك بشكل جميل وفحصها.