كيفية الحصول على أسماء enum في TypeScript: السحر تحت الغطاء

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: السؤال الأبدي على Stack Overflow

\n\n

هل تساءلت يومًا كيف يحصل المطورون المتمرسون في TypeScript على أسماء قيم enum؟ إذا كنت قد ألقيت نظرة على Stack Overflow، فمن المحتمل أنك رأيت سؤالًا بآلاف المشاهدات: "How to get names of enum entries?". قد يبدو الأمر بسيطًا؟ لكن في الممارسة العملية، يتعثر العديد من المبتدئين (وليس فقط) في هذه النقطة.

\n\n

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

\n\n

ما هو enum في TypeScript ولماذا نحتاج إلى أسمائها؟

\n\n

Enum (التعداد) هو طريقة ملائمة لتحديد مجموعة من الثوابت المسماة. في TypeScript، تكون إما رقمية (افتراضيًا) أو نصية. ولكن ماذا تفعل إذا كنت بحاجة إلى الحصول على اسم قيمة معينة؟ على سبيل المثال، تلقيت من API رمز الحالة 403، وتريد عرض رسالة مفهومة للمستخدم مثل "Forbidden".

\n\n
enum HttpStatus {\n  OK = 200,\n  NotFound = 404,\n  Forbidden = 403\n}\n\n// مهمتنا: الحصول على اسم "Forbidden" بالقيمة 403\n
\n\n

يبدو أن TypeScript يجب أن يوفر طريقة مدمجة لهذا الغرض. لكن لا. يجب كتابة منطق خاص بنا. وهنا يبدأ الجزء المثير للاهتمام.

\n\n

الحل المباشر: Object.keys و Object.values

\n\n

الطريقة الأكثر وضوحًا هي استخدام دوال Object.keys() أو Object.values(). يكتب العديد من المبتدئين شيئًا كهذا:

\n\n
function getEnumName(enumObj: any, value: number): string | undefined {\n  const keys = Object.keys(enumObj);\n  return keys.find(key => enumObj[key] === value);\n}\n\nconsole.log(getEnumName(HttpStatus, 403)); // "Forbidden" — يعمل!\n
\n\n

يبدو منطقيًا؟ نعم. ولكن إذا جربت هذا الكود على enum رقمي، فستفاجأ. دعنا نرى كيف يتم ترجمة HttpStatus الخاص بنا.

\n\n

تحت الغطاء: ترجمة enum الرقمي

\n\n

عندما يترجم TypeScript enum الرقمي، يقوم بإنشاء كائن مع تخطيط عكسي (reverse mapping). إليك كيف يبدو JavaScript المترجم:

\n\n
var HttpStatus;\n(function (HttpStatus) {\n    HttpStatus[HttpStatus["OK"] = 200] = "OK";\n    HttpStatus[HttpStatus["NotFound"] = 404] = "NotFound";\n    HttpStatus[HttpStatus["Forbidden"] = 403] = "Forbidden";\n})(HttpStatus || (HttpStatus = {}));\n\n// في النهاية، يبدو كائن HttpStatus هكذا:\n// { \n//   "200": "OK", \n//   "404": "NotFound", \n//   "403": "Forbidden", \n//   "OK": 200, \n//   "NotFound": 404, \n//   "Forbidden": 403 \n// }\n
\n\n

هل ترى الحيلة؟ يضيف المترجم مفاتيح رقمية ومفاتيح نصية. لذلك، سيعيد Object.keys(HttpStatus) ليس فقط ["OK", "NotFound", "Forbidden"]، بل أيضًا ["200", "404", "403"]. هذا يكسر منطق حلنا البسيط!

\n\n

لتجاوز ذلك، نحتاج إلى تصفية المفاتيح الرقمية:

\n\n
function getEnumNameSafe(enumObj: any, value: number): string | undefined {\n  const keys = Object.keys(enumObj).filter(key => isNaN(Number(key)));\n  return keys.find(key => enumObj[key] === value);\n}\n
\n\n

لكن هذا الكود يبدو غير أنيق. هل هناك طريقة أفضل؟

\n\n

enum النصي: قصة مختلفة

\n\n

إذا كنت تستخدم enum نصي، فلن يتم إنشاء تخطيط عكسي. هذا يبسط المهمة:

\n\n
enum Direction {\n  Up = "UP",\n  Down = "DOWN",\n  Left = "LEFT",\n  Right = "RIGHT"\n}\n\n// Object.keys(Direction) سيعيد فقط ["Up", "Down", "Left", "Right"] — مثالي!\n
\n\n

ولكن ماذا لو كنت بحاجة إلى الحصول على الاسم بناءً على القيمة؟ نفس Object.keys يعمل بشكل ممتاز:

\n\n
function getStringEn                

المدونات

توصيات الكتب