لماذا Generics؟
Generics تسمح لك بكتابة كود يعمل مع أنواع مختلفة بدون فقدان أمان الأنواع.
مشكلة بدون Generics
// تُعيد كل شيء كـ any — فقدنا النوع!
function firstItem(arr: any[]): any {
return arr[0];
}
const nums = [1, 2, 3];
const first = firstItem(nums); // first: any ❌الحلّ بـ Generics
function firstItem<T>(arr: T[]): T {
return arr[0];
}
const first = firstItem([1, 2, 3]); // number ✅
const name = firstItem(["أ", "ب"]); // string ✅Generics في الدوال
// Generic عام
function identity<T>(value: T): T {
return value;
}
// مع قيود (extends)
function logLength<T extends { length: number }>(item: T): T {
console.log(item.length);
return item;
}
logLength("hello"); // ✅ string له length
logLength([1, 2, 3]); // ✅ array له length
logLength(42); // ❌ number ليس له lengthGenerics في الـ interfaces
interface ApiResponse<T> {
data: T;
error: string | null;
status: number;
}
interface User { id: number; name: string; }
interface Post { id: number; title: string; }
const userRes: ApiResponse<User> = {
data: { id: 1, name: "أحمد" },
error: null,
status: 200,
};
const postRes: ApiResponse<Post[]> = {
data: [{ id: 1, title: "مرحبا" }],
error: null,
status: 200,
};Utility Types الأساسية
TypeScript يأتي بعشرات Utility Types جاهزة:
Partial — كل الحقول اختيارية
interface User {
id: number;
name: string;
email: string;
}
type UserUpdate = Partial<User>;
// { id?: number; name?: string; email?: string; }
function updateUser(id: number, updates: Partial<User>) {
// ...
}
updateUser(1, { name: "جديد" }); // ✅Required — عكس Partial
interface Config {
host?: string;
port?: number;
}
type StrictConfig = Required<Config>;
// { host: string; port: number; }Pick — خذ حقولاً محدّدة
type UserPublic = Pick<User, "id" | "name">;
// { id: number; name: string; }Omit — أزل حقولاً
type UserWithoutEmail = Omit<User, "email">;
// { id: number; name: string; }Record — كائن بأنواع محدّدة
type Permissions = Record<string, boolean>;
// أيّ string key → boolean
const perms: Permissions = {
read: true,
write: false,
delete: false,
};
// مع قيود صارمة
type Status = "pending" | "active" | "done";
type Counts = Record<Status, number>;
// { pending: number; active: number; done: number; }ReturnType — استخرج نوع الإرجاع
function getUser() {
return { id: 1, name: "x" };
}
type User = ReturnType<typeof getUser>;
// { id: number; name: string; }Conditional Types
"إذا كان X، فالنوع Y، وإلا Z":
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<number>; // falseمثال عملي: استخراج نوع المصفوفة
type ElementType<T> = T extends (infer U)[] ? U : T;
type A = ElementType<string[]>; // string
type B = ElementType<number[]>; // number
type C = ElementType<boolean>; // booleaninfer U تستخرج النوع الداخلي.
Mapped Types
إنشاء نوع جديد من نوع موجود:
// اجعل كل الحقول قابلة للقراءة فقط
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// اجعلها nullable
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
type User = { id: number; name: string; };
type NullableUser = Nullable<User>;
// { id: number | null; name: string | null; }Template Literal Types
type Greeting = `hello ${string}`;
const g1: Greeting = "hello world"; // ✅
const g2: Greeting = "hi world"; // ❌
// أمثلة أكثر قوّة
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint = `/api/${string}`;
type Route = `${HttpMethod} ${Endpoint}`;
const route: Route = "GET /api/users"; // ✅Discriminated Unions
قوّة TypeScript الحقيقية:
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number }
| { kind: "triangle"; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "triangle":
return 0.5 * shape.base * shape.height;
}
}TypeScript يعرف في كل case أيّ حقول متوفّرة.
مثال حقيقي: API Client type-safe
type Endpoints = {
"/users": { GET: { response: User[] } };
"/users/:id": {
GET: { response: User };
PUT: { body: Partial<User>; response: User };
DELETE: { response: void };
};
};
async function api<
Path extends keyof Endpoints,
Method extends keyof Endpoints[Path]
>(
path: Path,
method: Method,
options?: Endpoints[Path][Method] extends { body: infer B } ? { body: B } : {}
): Promise<
Endpoints[Path][Method] extends { response: infer R } ? R : never
> {
// ... implementation
}
// استخدام — TypeScript يفرض كل شيء
const users = await api("/users", "GET");
const user = await api("/users/:id", "PUT", { body: { name: "جديد" } });الأسئلة الشائعة
متى استخدم Generics؟
عند كتابة كود قابل لإعادة الاستخدام مع أنواع مختلفة — helpers، components، data structures.
Generics vs any؟
any يتخلّى عن الأنواع. Generics تحافظ عليها مع الحفاظ على المرونة.
كم Generic parameter يمكن؟
تقنياً لا حدّ، لكن > 3 يصبح صعب القراءة. فكّر في إعادة التصميم.
مقالات ذات صلة
OOP في Python: البرمجة الكائنية بأمثلة عملية
البرمجة الكائنية تنظّم كودك وتسهّل صيانته. تعلّم الكلاسات، الوراثة، والتغليف في Python.
شرح Async و Await في JavaScript بأمثلة عملية
افهم async/await في JavaScript من أول مرة — بدون callbacks وبدون سلاسل then. أمثلة حقيقية من جلب البيانات للتحكّم بالأخطاء.
Python للمبتدئين بالعربي: درس شامل من الصفر
ابدأ تعلّم Python من الصفر بالعربية: المتغيّرات، الشروط، الحلقات، الدوال، والقوائم. أمثلة عملية وأخطاء شائعة.