العربية

استكشف اتحادات التمييز في TypeScript، أداة قوية لبناء آلات حالة قوية وآمنة من حيث النوع. تعرف على كيفية تحديد الحالات، ومعالجة الانتقالات، والاستفادة من نظام أنواع TypeScript لزيادة موثوقية التعليمات البرمجية.

اتحاد التمييز في TypeScript: بناء آلات الحالة الآمنة من حيث النوع

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

ما هي اتحادات التمييز؟

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

فكر في الأمر مثل إشارة المرور. يمكن أن تكون في إحدى ثلاث حالات: أحمر، أصفر، أو أخضر. تعمل خاصية 'اللون' كمميز، مما يخبرنا بالضبط أي حالة تشغلها الإشارة.

لماذا نستخدم اتحادات التمييز لآلات الحالة؟

تجلب اتحادات التمييز العديد من الفوائد الرئيسية عند بناء آلات الحالة في TypeScript:

تحديد آلة الحالة باستخدام اتحادات التمييز

دعنا نوضح كيفية تحديد آلة الحالة باستخدام اتحادات التمييز مع مثال عملي: نظام معالجة الطلبات. يمكن أن يكون الطلب في الحالات التالية: معلق، قيد المعالجة، تم الشحن، و تم التسليم.

الخطوة 1: تحديد أنواع الحالة

أولاً، نحدد الأنواع الفردية لكل حالة. سيكون لكل نوع خاصية `type` تعمل كمميز، بالإضافة إلى أي بيانات خاصة بالحالة.


interface Pending {
  type: "pending";
  orderId: string;
  customerName: string;
  items: string[];
}

interface Processing {
  type: "processing";
  orderId: string;
  assignedAgent: string;
}

interface Shipped {
  type: "shipped";
  orderId: string;
  trackingNumber: string;
}

interface Delivered {
  type: "delivered";
  orderId: string;
  deliveryDate: Date;
}

الخطوة 2: إنشاء نوع اتحاد التمييز

بعد ذلك، نقوم بإنشاء اتحاد التمييز عن طريق تجميع هذه الأنواع الفردية باستخدام عامل التشغيل `|` (اتحاد).


type OrderState = Pending | Processing | Shipped | Delivered;

الآن، يمثل `OrderState` قيمة يمكن أن تكون إما `Pending` أو `Processing` أو `Shipped` أو `Delivered`. تعمل خاصية `type` داخل كل حالة كمميز، مما يسمح لـ TypeScript بالتمييز بينها.

معالجة انتقالات الحالة

الآن بعد أن حددنا آلة الحالة الخاصة بنا، نحتاج إلى آلية للانتقال بين الحالات. دعنا ننشئ دالة `processOrder` تأخذ الحالة الحالية وإجراء كمدخلات وتعيد الحالة الجديدة.


interface Action {
  type: string;
  payload?: any;
}

function processOrder(state: OrderState, action: Action): OrderState {
  switch (state.type) {
    case "pending":
      if (action.type === "startProcessing") {
        return {
          type: "processing",
          orderId: state.orderId,
          assignedAgent: action.payload.agentId,
        };
      }
      return state; // لا تغيير في الحالة

    case "processing":
      if (action.type === "shipOrder") {
        return {
          type: "shipped",
          orderId: state.orderId,
          trackingNumber: action.payload.trackingNumber,
        };
      }
      return state; // لا تغيير في الحالة

    case "shipped":
      if (action.type === "deliverOrder") {
        return {
          type: "delivered",
          orderId: state.orderId,
          deliveryDate: new Date(),
        };
      }
      return state; // لا تغيير في الحالة

    case "delivered":
      // تم تسليم الطلب بالفعل، لا مزيد من الإجراءات
      return state;

    default:
      // لا ينبغي أن يحدث هذا أبدًا بسبب التحقق من الشمولية
      return state; // أو إلقاء خطأ
  }
}

شرح

الاستفادة من التحقق من الشمولية

يعد التحقق من شمولية TypeScript ميزة قوية تضمن معالجة جميع الحالات الممكنة في آلة الحالة الخاصة بك. إذا أضفت حالة جديدة إلى اتحاد `OrderState` ولكن نسيت تحديث الدالة `processOrder`، فستشير TypeScript إلى خطأ.

لتمكين التحقق من الشمولية، يمكنك استخدام النوع `never`. داخل حالة `default` لعبارة `switch` الخاصة بك، قم بتعيين الحالة لمتغير من النوع `never`.


function processOrder(state: OrderState, action: Action): OrderState {
  switch (state.type) {
    // ... (الحالات السابقة) ...

    default:
      const _exhaustiveCheck: never = state;
      return _exhaustiveCheck; // أو إلقاء خطأ
  }
}

إذا كانت عبارة `switch` تعالج جميع قيم `OrderState` الممكنة، فسيكون المتغير `_exhaustiveCheck` من النوع `never` وسيترجم الكود. ومع ذلك، إذا أضفت حالة جديدة إلى اتحاد `OrderState` ونسيت معالجتها في عبارة `switch`، فسيكون المتغير `_exhaustiveCheck` من نوع مختلف، وسترمي TypeScript خطأ في وقت الترجمة، مما ينبهك إلى الحالة المفقودة.

أمثلة وتطبيقات عملية

تنطبق اتحادات التمييز في مجموعة واسعة من السيناريوهات بخلاف معالجة الطلبات البسيطة:

مثال: إدارة حالة واجهة المستخدم

دعنا ننظر في مثال بسيط لإدارة حالة مكون واجهة مستخدم يجلب البيانات من واجهة برمجة التطبيقات. يمكننا تعريف الحالات التالية:


interface Initial {
  type: "initial";
}

interface Loading {
  type: "loading";
}

interface Success {
  type: "success";
  data: T;
}

interface Error {
  type: "error";
  message: string;
}

type UIState = Initial | Loading | Success | Error;

function renderUI(state: UIState): React.ReactNode {
  switch (state.type) {
    case "initial":
      return 

Click the button to load data.

; case "loading": return

Loading...

; case "success": return
{JSON.stringify(state.data, null, 2)}
; case "error": return

Error: {state.message}

; default: const _exhaustiveCheck: never = state; return _exhaustiveCheck; } }

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

أفضل الممارسات لاستخدام اتحادات التمييز

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

تقنيات متقدمة

الأنواع الشرطية

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


function getData(state: UIState): T | undefined {
  if (state.type === "success") {
    return state.data;
  }
  return undefined;
}

تستخدم هذه الدالة عبارة `if` بسيطة ولكن يمكن جعلها أكثر قوة باستخدام الأنواع الشرطية لضمان إرجاع نوع معين دائمًا.

الأنواع المساعدة

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


// استخراج حالة "success" من اتحاد UIState
type SuccessState = Extract, { type: "success" }>;

// إزالة خاصية 'message' من واجهة Error
type ErrorWithoutMessage = Omit;

أمثلة واقعية عبر مختلف الصناعات

تمتد قوة اتحادات التمييز عبر مختلف الصناعات ومجالات التطبيق:

الخلاصة

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

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