🛡️ احتراف الـ Type Guards في TypeScript: كيف تتحكم في أنواع بياناتك؟
في الدروس السابقة، تعلمنا كيف نحدد أنواع البيانات (Types). ولكن ماذا يحدث عندما يكون لدينا متغير يمكن أن يحمل "أكثر من نوع" في نفس الوقت؟ هنا تظهر مشكلة: كيف نخبر TypeScript بأن هذا المتغير الآن هو من نوع معين حتى نتمكن من استخدام الخصائص الخاصة بهذا النوع دون أن يظهر لنا خطأ؟
هنا يأتي دور Type Guards (حراس الأنواع). ببساطة، هي طريقة لإخبار TypeScript: "تأكد أولاً أن هذا المتغير هو من نوع (كذا)، وإذا كان كذلك، اسمح لي باستخدام ميزاته".
🧐 ما هي مشكلة "تعدد الأنواع" (Union Types)؟
قبل أن نشرح الحل، دعنا نرى المشكلة. تخيل أن لديك دالة تستقبل إما نصاً (string) أو رقماً (number).
function printLength(value: string | number) {
// هنا سيقوم TypeScript بإعطائك خطأ! ❌
// لأن خاصية length موجودة في النصوص فقط وليست في الأرقام
console.log(value.length);
}
في المثال أعلاه، TypeScript يرفض تشغيل الكود لأنه لا يضمن أن value ستكون دائماً نصاً. ماذا لو كانت رقماً؟ الأرقام ليس لها length! 😱
الحل هو استخدام Type Guards لعمل "تضييق" (Narrowing) للنوع.
1️⃣ استخدام typeof (لأنواع البيانات الأساسية) 🔍
تعتبر typeof هي الطريقة الأبسط والأكثر استخداماً للتحقق من الأنواع الأساسية مثل (string, number, boolean).
لنقم بتعديل المثال السابق:
function printLength(value: string | number) {
if (typeof value === "string") {
// داخل هذا القوس، TypeScript متأكد 100% أن value هي string
console.log("The length is: " + value.length); // ✅ تعمل الآن بنجاح
} else {
// هنا TypeScript يستنتج تلقائياً أن value يجب أن تكون number
console.log("This is a number, it has no length!"); // ✅
}
}
printLength("Hello"); // Output: The length is: 5
printLength(100); // Output: This is a number, it has no length!
ماذا حدث هنا؟ 💡
بمجرد كتابة if (typeof value === "string") قام TypeScript بعملية تسمى Type Narrowing. أي أنه "ضيّق" الاحتمالات، فداخل الـ if أصبح المتغير نصاً فقط.
2️⃣ استخدام instanceof (للكائنات والصفوف) 🏗️
عندما نتعامل مع كائنات (Objects) أو صفوف (Classes)، لا يمكننا استخدام typeof لأنها ستخبرنا ببساطة أن النوع هو "object". هنا نستخدم instanceof.
مثال بسيط: لنفترض أن لدينا صنفاً (Class) يمثل "مستخدم".
class User {
name: string = "Guest";
}
class Admin {
role: string = "Super Admin";
}
function checkUserRole(person: User | Admin) {
if (person instanceof Admin) {
// هنا TypeScript يعرف أن الشخص هو Admin
console.log("Welcome Admin! Your role is: " + person.role); // ✅
} else {
// هنا TypeScript يعرف أنه User عادي
console.log("Welcome User! Your name is: " + person.name); // ✅
}
}
const user1 = new User();
const admin1 = new Admin();
checkUserRole(user1); // Output: Welcome User! Your name is: Guest
checkUserRole(admin1); // Output: Welcome Admin! Your role is: Super Admin
3️⃣ التحقق من وجود خاصية معينة (In Operator) 🔑
أحياناً لا نملك class أو نوعاً أساسياً، بل نملك "كائنات" مختلفة ونريد التأكد من وجود خاصية معينة بداخلها باستخدام الكلمة المحجوزة in.
مثال توضيحي:
interface Fish {
swim: () => void;
}
interface Bird {
fly: () => void;
}
function move(animal: Fish | Bird) {
if ("swim" in animal) {
// إذا كانت خاصية swim موجودة، إذن هذا Fish
animal.swim(); // ✅
} else {
// خلاف ذلك، هو Bird
animal.fly(); // ✅
}
}
const myFish = { swim: () => console.log("Swimming...") };
const myBird = { fly: () => console.log("Flying...") };
move(myFish); // Output: Swimming...
move(myBird); // Output: Flying...
📝 ملخص سريع للـ Type Guards
| الأداة | متى نستخدمها؟ | مثال |
|---|---|---|
typeof |
مع الأنواع الأساسية (string, number, etc) | typeof val === 'string' |
instanceof |
مع الصفوف (Classes) والكائنات المنشأة منها | val instanceof User |
in |
للتأكد من وجود خاصية (Property) داخل كائن | 'name' in val |
🎓 اختبر معلوماتك
التعليقات
شاركنا رأيك أو أسئلتك حول هذا المقال