القواميس في TypeScript: من الفوضى إلى النظام (دليل كامل مع كود)
\n\nتخيل أنك تكتب تطبيقًا لتعلم اللغة الإنجليزية. تحتاج إلى تخزين ترجمات الكلمات: "cat" → "قطة", "dog" → "كلب". ما هي أفضل طريقة لتنظيم هذه البيانات؟ بالطبع، استخدام قاموس (Dictionary) — هيكل بيانات حيث يكون كل مفتاح فريدًا ومرتبطًا بقيمة.
في JavaScript، يتم تنفيذ القواميس من خلال الكائنات العادية أو Map. لكن TypeScript يضيف كتابة قوية، محولاً المجموعات الفوضوية إلى هياكل صارمة وقابلة للتنبؤ. في هذه المقالة، سنشرح الإعلان والتهيئة للقاموس في TypeScript من البسيط إلى المعقد. ستتعلم كيفية تجنب الأخطاء النموذجية وكتابة كود يعمل بشكل متوقع.
ما هو القاموس (Dictionary) في TypeScript؟ (ولماذا ليس مجرد كائن)
\n\nفي البرمجة، Dictionary (قاموس) هو مجموعة من أزواج «مفتاح-قيمة». في TypeScript، يمكنك إنشاؤه بثلاث طرق رئيسية:
\n\n- \n
- كائن مع توقيع فهرس (
{ [key: string]: T }) \n - Record — نوع مساعد \n
- Map — فئة ES6 المدمجة \n
لكل نهج مزاياه وملاحظاته. دعنا نناقشها بأمثلة حقيقية.
\n\nالطريقة 1: الكائن الكلاسيكي مع توقيع الفهرس
\n\nالطريقة الأكثر شيوعًا لإعلان قاموس في TypeScript هي استخدام توقيع الفهرس. هذا يخبر المترجم: «يمكن أن يحتوي الكائن على أي عدد من الخصائص بمفاتيح من نوع معين».
\n\n// نعلن نوع القاموس، حيث المفتاح سلسلة والقيمة رقم\ninterface DictionaryNumber {\n [key: string]: number;\n}\n\n// التهيئة\nconst ages: DictionaryNumber = {\n "آنا": 25,\n "بيتر": 30,\n "إيلينا": 28\n};\n\n// إضافة عنصر جديد\nages["أولغا"] = 35;\n\nconsole.log(ages["آنا"]); // 25\n\nمهم: TypeScript يتطلب أن تتطابق جميع القيم مع النوع المُعلن. محاولة إضافة سلسلة ستسبب خطأ في الترجمة:
\n\n// خطأ! لا يمكن تعيين النوع 'string' للنوع 'number'\nages["أولغا"] = "خمسة وثلاثون";\n\nمتى تستخدم هذا النهج؟
\n- \n
- عندما تحتاج إلى قاموس بسيط وسريع \n
- عندما تكون المفاتيح سلاسل أو أرقامًا \n
- للمجموعات الصغيرة (حتى 1000 عنصر) \n
الطريقة 2: Record — بناء جملة أنيق
\n\nيوفر TypeScript نوعًا مساعدًا مدمجًا Record<K, V>. يقوم بإنشاء نوع كائن حيث جميع المفاتيح من النوع K والقيم من النوع V.
// الإعلان والتهيئة في سطر واحد\nconst grades: Record<string, number> = {\n "الرياضيات": 5,\n "الفيزياء": 4,\n "التاريخ": 5\n};\n\n// يمكن استخدام enum للمفاتيح\nenum Subject {\n Math = "math",\n Physics = "physics",\n History = "history"\n}\n\nconst gradesBySubject: Record<Subject, number> = {\n [Subject.Math]: 5,\n [Subject.Physics]: 4,\n [Subject.History]: 5\n};\n\nمزايا Record:
\n- \n
- بناء جملة مختصر \n
- قراءة جيدة \n
- دعم أنواع الاتحاد للمفاتيح \n
الطريقة 3: Map — للسيناريوهات المتقدمة
\n\nفئة Map من ES6 هي مجموعة كاملة من «مفتاح-قيمة» مع إمكانيات إضافية. في TypeScript، يتم كتابتها باستخدام الأدوية (Generics):
// إعلان Map\nconst userRoles: Map<string, string> = new Map([\n ["admin", "مسؤول"],\n ["user", "مستخدم"],\n ["guest", "ضيف"]\n]);\n\n// إضافة\nuserRoles.set("moderator", "مشرف");\n\n// الحصول\nconsole.log(userRoles.get("admin")); // "مسؤول"\n\n// التحقق من الوجود\nif (userRoles.has("user")) {\n console.log("الدور موجود");\n}\n\n// و