الثوابت في فئات TypeScript: من المبتدئ إلى المحترف
\n\nهل سبق لك أن كتبت كودًا بلغة TypeScript وأدركت فجأة: «هذه السلسلة النصية السحرية "ADMIN" أستخدمها في عشرة أماكن، وإذا أخطأت في كتابتها - سيبقى المترجم صامتًا»؟ ألم مألوف. أو الأسوأ من ذلك، أن يقوم أحد أعضاء الفريق بتغيير قيمة الثابت مباشرة في وقت التشغيل، وتقضي ساعة في التصحيح لتفهم لماذا تعطل كل شيء.
\n\nمرحبًا بك في عالم ثوابت الفئات في TypeScript. هذا ليس مجرد سكر نحوي - بل هو أساس لبنية نظيفة وقابلة للتنبؤ وآمنة. في هذه المقالة، سنستعرض جميع طرق تعريف الثوابت في فئات TypeScript: من readonly البسيط إلى التقنيات المتقدمة مع as const والأجناس (Generics). هيا بنا!
لماذا الثوابت في الفئات مهمة؟
\n\nتخيل أنك تطور تطبيقًا لمتجر إلكتروني. لديك فئة Order، وفيها حالات: "pending"، "shipped"، "delivered". إذا قمت بتوزيع هذه السلاسل النصية في جميع أنحاء الكود، عاجلاً أم آجلاً سيكتب أحدهم "PENDING" بحرف كبير - وسيحدث الخطأ. ثوابت الفئات في TypeScript تحل هذه المشكلة مرة واحدة وإلى الأبد.
- \n
- أمان الأنواع: لن يسمح المترجم بتمرير "pendingg" حيثما يكون "pending" مطلوبًا. \n
- قابلية القراءة:
OrderStatus.Pendingأوضح من مجرد سلسلة نصية. \n - سهولة الصيانة: تغيير القيمة في مكان واحد يغيرها في كل مكان. \n
الطريقة رقم 1: readonly - الأساس ولكن مع تفاصيل
\n\nأبسط طريقة هي استخدام المُعدِّل readonly. فهو يمنع تغيير الحقل بعد التهيئة.
class AppConfig {\n readonly API_URL: string = 'https://api.example.com';\n readonly MAX_RETRIES: number = 3;\n}\n\nconst config = new AppConfig();\nconsole.log(config.API_URL); // 'https://api.example.com'\n// config.API_URL = 'new-url'; // خطأ! لا يمكن التعيين لـ 'API_URL' لأنه خاصية للقراءة فقط.\n\nلكن هناك خدعة: readonly يعمل فقط على مستوى TypeScript. في JavaScript المُجمَّع، لا يزال من الممكن تغيير هذه الحقول. للحماية الكاملة، استخدم Object.freeze أو # (الحقول الخاصة).
الطريقة رقم 2: static readonly - ملك الثوابت
\n\nإذا كان الثابت غير مرتبط بمثيل معين من الفئة (وهذا يحدث في 99% من الحالات)، استخدم static readonly. هذا هو النمط الكلاسيكي class constant typescript.
class MathConstants {\n static readonly PI: number = 3.14159;\n static readonly E: number = 2.71828;\n static readonly GOLDEN_RATIO: number = 1.61803;\n}\n\n// الوصول دون إنشاء مثيل\nconsole.log(MathConstants.PI); // 3.14159\n// MathConstants.PI = 3.14; // خطأ!\n\nهذا النهج مثالي للإعدادات، والثوابت الرياضية، والحالات، وأي قيم لا ينبغي أن تتغير.
\n\nالطريقة رقم 3: enum - عندما تكون الثوابت كثيرة
\n\nإذا كان لديك مجموعة من الثوابت المرتبطة (مثل حالات الطلب)، فإن enum هو أفضل صديق لك. فهو ينشئ مجموعة مسماة من القيم.
enum OrderStatus {\n Pending = 'pending',\n Shipped = 'shipped',\n Delivered = 'delivered',\n Cancelled = 'cancelled'\n}\n\nclass Order {\n constructor(public status: OrderStatus) {}\n}\n\nconst order = new Order(OrderStatus.Pending);\nconsole.log(order.status); // 'pending'\n\n// order.status = 'pendingg'; // خطأ! النوع '"pendingg"' غير قابل للإسناد للنوع 'OrderStatus'\n\nنصيحة: إذا كنت تستخدم enum نصيًا (كما في المثال)، فإنه يعطي قيمًا قابلة للقراءة في وقت التشغيل. enum الرقمية أسرع، ولكن يصعب تصحيح أخطائها.
الطريقة رقم 4: as const - لأقصى درجات الصرامة
\n\nأحيانًا تريد أن يصبح الكائن أو المصفوفة ثا