🚀 احتراف الـ Utility Types في TypeScript: اجعل أكوادك أكثر مرونة!
بعد أن تعلمت كيف تنشئ الأنواع (Types) والواجهات (Interfaces)، قد تجد نفسك أحياناً في موقف تحتاج فيه إلى نسخة "معدلة" من نوع موجود مسبقاً. على سبيل المثال: ماذا لو كان لديك نوع يمثل "المستخدم"، ولكنك تريد نوعاً آخر يحتوي على نفس البيانات ولكن جميع الخصائص فيه "اختيارية"؟
هنا يأتي دور Utility Types. وهي ببساطة "أدوات جاهزة" توفرها لغة TypeScript لمساعدتنا في تحويل الأنواع من شكل إلى آخر دون الحاجة لإعادة كتابتها من الصفر. 🛠️
1️⃣ النوع Partial: جعل كل شيء اختيارياً 🎈
تخيل أن لديك واجهة (Interface) تمثل مستخدماً، وبها بيانات أساسية. عند تحديث بيانات المستخدم، قد لا يقوم المستخدم بتغيير كل بياناته، بل يغير "الاسم فقط" أو "الإيميل فقط".
بدلاً من إنشاء نوع جديد يحتوي على علامات استفهام ? بجانب كل خاصية، نستخدم Partial.
مثال عملي:
interface User {
id: number;
username: string;
email: string;
}
// هنا نستخدم Partial لجعل جميع خصائص User اختيارية
type UpdateUserDto = Partial<User>;
// الآن يمكننا إنشاء كائن يحتوي على خاصية واحدة فقط دون اعتراض TypeScript
const updateData: UpdateUserDto = {
username: "codex_hero" // لا يشترط وجود id أو email هنا
};
💡 ماذا حدث هنا؟
Partial<User> قامت بتحويل النوع من:
{ id: number; username: string; email: string; }
إلى:
{ id?: number; username?: string; email?: string; }
2️⃣ النوع Readonly: حماية البيانات من التعديل 🛡️
في بعض الأحيان، تريد إنشاء كائن يحتوي على بيانات "للقراءة فقط"، بحيث إذا حاول أي شخص تغيير قيمة أي خاصية، يقوم TypeScript بتنبيهه فوراً ومنعه من ذلك. هذا يساعد في تجنب الأخطاء غير المقصودة في المشاريع الكبيرة.
مثال عملي:
interface Config {
apiUrl: string;
port: number;
}
// تحويل الخصائص إلى "للقراءة فقط"
const appConfig: Readonly<Config> = {
apiUrl: "https://api.codex.com",
port: 8080
};
// الخطأ القادم سيظهر لأننا حاولنا تعديل خاصية Readonly
// appConfig.port = 3000; // ❌ TypeScript Error: Cannot assign to 'port' because it is a read-only property.
💡 ماذا حدث هنا؟
Readonly<Config> جعلت جميع الخصائص داخل الكائن غير قابلة للتعديل بعد عملية التعريف الأولى.
3️⃣ النوع Pick: اختيار خصائص محددة 🎯
أحياناً يكون لديك نوع ضخم يحتوي على 20 خاصية، ولكنك تحتاج فقط إلى خاصيتين منهما لاستخدامهما في وظيفة (Function) معينة. بدلاً من كتابة نوع جديد، نستخدم Pick.
مثال عملي:
interface Product {
id: number;
name: string;
price: number;
description: string;
category: string;
}
// نريد فقط الاسم والسعر من واجهة المنتج
type ProductPreview = Pick<Product, "name" | "price">;
const simpleProduct: ProductPreview = {
name: "Gaming Mouse",
price: 50
};
// لا يمكن إضافة description هنا لأننا اخترنا name و price فقط
💡 ماذا حدث هنا؟
Pick<Product, "name" | "price"> أخبرت TypeScript: "اذهب إلى Product وخذ منها فقط name و price وتجاهل البقية".
4️⃣ النوع Omit: استبعاد خصائص محددة 🚫
الـ Omit هي عكس الـ Pick تماماً. بدلاً من اختيار ما تريد، أنت تخبر TypeScript بما لا تريده. هي مفيدة جداً عندما تريد نسخة من النوع ولكن بدون خاصية معينة (مثل حذف كلمة المرور من بيانات المستخدم قبل إرسالها للواجهة الأمامية).
مثال عملي:
interface Employee {
id: number;
fullName: string;
salary: number;
department: string;
}
// نريد كل البيانات ما عدا الراتب (salary)
type EmployeePublicInfo = Omit<Employee, "salary">;
const employeeInfo: EmployeePublicInfo = {
id: 101,
fullName: "Ahmed Ali",
department: "IT"
// salary: 5000 // ❌ سيحدث خطأ لأننا استبعدنا الراتب
};
💡 ماذا حدث هنا؟
Omit<Employee, "salary"> قامت بإنشاء نوع جديد يحتوي على كل شيء من Employee باستثناء salary.
📋 ملخص سريع للأدوات التي تعلمناها:
| الأداة | الوظيفة باختصار |
|---|---|
Partial<T> |
تجعل كل الخصائص اختيارية (?). |
Readonly<T> |
تمنع تعديل الخصائص بعد تعريفها. |
Pick<T, K> |
تختار مجموعة محددة من الخصائص. |
Omit<T, K> |
تحذف مجموعة محددة من الخصائص. |
🎓 اختبر معلوماتك
التعليقات
شاركنا رأيك أو أسئلتك حول هذا المقال