العربية

استكشف حارسات الأنواع وتأكيدات الأنواع في TypeScript لتعزيز أمان الأنواع، ومنع أخطاء وقت التشغيل، وكتابة كود أكثر قوة وقابلية للصيانة. تعلم مع أمثلة عملية وأفضل الممارسات.

إتقان أمان الأنواع: دليل شامل لحارسات الأنواع وتأكيدات الأنواع

في عالم تطوير البرمجيات، خاصة عند العمل مع لغات ديناميكية الأنواع مثل JavaScript، يمكن أن يمثل الحفاظ على أمان الأنواع تحديًا كبيرًا. تعالج TypeScript، وهي مجموعة شاملة من JavaScript، هذا القلق عن طريق إدخال الكتابة الساكنة. ومع ذلك، حتى مع نظام الأنواع في TypeScript، تنشأ مواقف يحتاج فيها المترجم إلى المساعدة في استنتاج النوع الصحيح للمتغير. هذا هو المكان الذي تلعب فيه حارسات الأنواع (type guards) وتأكيدات الأنواع (type assertions) دورًا. سيغوص هذا الدليل الشامل في هذه الميزات القوية، ويقدم أمثلة عملية وأفضل الممارسات لتعزيز موثوقية التعليمات البرمجية الخاصة بك وقابليتها للصيانة.

ما هي حارسات الأنواع (Type Guards)؟

حارسات الأنواع هي تعبيرات TypeScript التي تضيق نوع المتغير ضمن نطاق معين. إنها تمكن المترجم من فهم نوع المتغير بدقة أكبر مما استنتجه في البداية. هذا مفيد بشكل خاص عند التعامل مع أنواع الاتحاد (union types) أو عندما يعتمد نوع المتغير على ظروف وقت التشغيل. باستخدام حارسات الأنواع، يمكنك تجنب أخطاء وقت التشغيل وكتابة كود أكثر قوة.

تقنيات حارسات الأنواع الشائعة

توفر TypeScript العديد من الآليات المدمجة لإنشاء حارسات الأنواع:

استخدام typeof

المعامل typeof هو طريقة مباشرة للتحقق من النوع البدائي للمتغير. يعيد سلسلة نصية تشير إلى النوع.

function printValue(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase()); // TypeScript يعرف أن 'value' هو سلسلة نصية هنا
  } else {
    console.log(value.toFixed(2)); // TypeScript يعرف أن 'value' هو رقم هنا
  }
}

printValue("hello"); // المخرجات: HELLO
printValue(3.14159); // المخرجات: 3.14

استخدام instanceof

يتحقق المعامل instanceof مما إذا كان الكائن هو نسخة من فئة معينة. هذا مفيد بشكل خاص عند العمل مع الوراثة.

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

class Dog extends Animal {
  bark() {
    console.log("Woof!");
  }
}

function makeSound(animal: Animal) {
  if (animal instanceof Dog) {
    animal.bark(); // TypeScript يعرف أن 'animal' هو من نوع Dog هنا
  } else {
    console.log("Generic animal sound");
  }
}

const myDog = new Dog("Buddy");
const myAnimal = new Animal("Generic Animal");

makeSound(myDog); // المخرجات: Woof!
makeSound(myAnimal); // المخرجات: Generic animal sound

استخدام in

يتحقق المعامل in مما إذا كان الكائن يحتوي على خاصية معينة. هذا مفيد عند التعامل مع الكائنات التي قد تحتوي على خصائص مختلفة اعتمادًا على نوعها.

interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

function move(animal: Bird | Fish) {
  if ("fly" in animal) {
    animal.fly(); // TypeScript يعرف أن 'animal' هو من نوع Bird هنا
  } else {
    animal.swim(); // TypeScript يعرف أن 'animal' هو من نوع Fish هنا
  }
}

const myBird: Bird = { fly: () => console.log("Flying"), layEggs: () => console.log("Laying eggs") };
const myFish: Fish = { swim: () => console.log("Swimming"), layEggs: () => console.log("Laying eggs") };

move(myBird); // المخرجات: Flying
move(myFish); // المخرجات: Swimming

دوال حارسات الأنواع المخصصة

لسيناريوهات أكثر تعقيدًا، يمكنك تحديد دوال حارسات الأنواع الخاصة بك. تعيد هذه الدوال مسندًا للنوع (type predicate)، وهو تعبير منطقي تستخدمه TypeScript لتضييق نوع المتغير. يأخذ مسند النوع الشكل المتغير is النوع.

interface Square {
  kind: "square";
  size: number;
}

interface Circle {
  kind: "circle";
  radius: number;
}

type Shape = Square | Circle;

function isSquare(shape: Shape): shape is Square {
  return shape.kind === "square";
}

function getArea(shape: Shape) {
  if (isSquare(shape)) {
    return shape.size * shape.size; // TypeScript يعرف أن 'shape' هو من نوع Square هنا
  } else {
    return Math.PI * shape.radius * shape.radius; // TypeScript يعرف أن 'shape' هو من نوع Circle هنا
  }
}

const mySquare: Square = { kind: "square", size: 5 };
const myCircle: Circle = { kind: "circle", radius: 3 };

console.log(getArea(mySquare)); // المخرجات: 25
console.log(getArea(myCircle)); // المخرجات: 28.274333882308138

ما هي تأكيدات الأنواع (Type Assertions)؟

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

تأكيدات الأنواع لها شكلان:

الكلمة المفتاحية as مفضلة بشكل عام لأنها أكثر توافقًا مع JSX.

متى تستخدم تأكيدات الأنواع؟

تُستخدم تأكيدات الأنواع عادةً في السيناريوهات التالية:

أمثلة على تأكيدات الأنواع

تأكيد النوع الصريح

في هذا المثال، نؤكد أن استدعاء document.getElementById سيعيد HTMLCanvasElement. بدون التأكيد، ستستنتج TypeScript نوعًا أكثر عمومية وهو HTMLElement | null.

const canvas = document.getElementById("myCanvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d"); // TypeScript يعرف أن 'canvas' هو من نوع HTMLCanvasElement هنا

if (ctx) {
  ctx.fillStyle = "#FF0000";
  ctx.fillRect(0, 0, 150, 75);
}

التعامل مع أنواع غير معروفة

عند العمل مع بيانات من مصدر خارجي، مثل واجهة برمجة التطبيقات (API)، قد تتلقى بيانات بنوع غير معروف. يمكنك استخدام تأكيد النوع لإخبار TypeScript بكيفية التعامل مع البيانات.

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

async function fetchUser(id: number): Promise<User> {
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
  const data = await response.json();
  return data as User; // تأكيد أن البيانات هي من نوع User
}

fetchUser(1)
  .then(user => {
    console.log(user.name); // TypeScript يعرف أن 'user' هو من نوع User هنا
  })
  .catch(error => {
    console.error("Error fetching user:", error);
  });

تحذيرات عند استخدام تأكيدات الأنواع

يجب استخدام تأكيدات الأنواع باعتدال وبحذر. يمكن أن يؤدي الإفراط في استخدام تأكيدات الأنواع إلى إخفاء أخطاء الأنواع الأساسية ويؤدي إلى مشكلات في وقت التشغيل. إليك بعض الاعتبارات الرئيسية:

تضييق الأنواع (Type Narrowing)

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

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

مثال على تضييق الأنواع

function processValue(value: string | number | null) {
  if (value === null) {
    console.log("Value is null");
  } else if (typeof value === "string") {
    console.log(value.toUpperCase()); // TypeScript يعرف أن 'value' هو سلسلة نصية هنا
  } else {
    console.log(value.toFixed(2)); // TypeScript يعرف أن 'value' هو رقم هنا
  }
}

processValue("test"); // المخرجات: TEST
processValue(123.456); // المخرجات: 123.46
processValue(null); // المخرجات: Value is null

أفضل الممارسات

للاستفادة بشكل فعال من حارسات الأنواع وتأكيدات الأنواع في مشاريع TypeScript الخاصة بك، ضع في اعتبارك أفضل الممارسات التالية:

الاعتبارات الدولية

عند تطوير تطبيقات لجمهور عالمي، كن على دراية بكيفية تأثير حارسات الأنواع وتأكيدات الأنواع على جهود التوطين والتدويل (i18n). على وجه التحديد، ضع في اعتبارك ما يلي:

الخاتمة

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