Get و Set في TypeScript: سحر الأكسيسورات بدون ألم
\n\nهل سبق لك أن كتبت كلاسًا في TypeScript وفكرت: "ليتني أستطيع جعل الخاصية ذكية، بحيث تحسب شيئًا عند القراءة، وتتحقق من البيانات عند الكتابة"؟ تعرف على الجيتّرات (get) والستّرات (set). إنها ليست مجرد سكر نحوي، بل أداة قوية للتحكم في الوصول إلى البيانات.
\n\nإذا كنت قد صادفت هذا الموضوع على Stack Overflow، فأنت تعلم أن سؤال "get and set in TypeScript" يجمع آلاف المشاهدات. يختلط الأمر على الناس متى يستخدمون الأكسيسورات ومتى يستخدمون الطرق العادية. في هذه المقالة، سنرتب كل شيء: من الأساسيات إلى التفاصيل الدقيقة.
\n\nهل أنت مستعد؟ لننطلق!
\n\nما هما get و set في TypeScript؟
\n\nالجيتّر هو طريقة تُستدعى عند قراءة خاصية. الستّر هو طريقة تُستدعى عند تعيين قيمة. معًا يشكلان أكسيسورات (accessors). يبدو الأمر كما لو كنت تتعامل مع حقل عادي، ولكن تحت الغطاء يوجد منطق.
\n\nإليك مثال بسيط:
\n\nclass User {\n private _name: string = '';\n\n get name(): string {\n return this._name;\n }\n\n set name(value: string) {\n if (value.length > 0) {\n this._name = value;\n } else {\n console.log('الاسم لا يمكن أن يكون فارغًا');\n }\n }\n}\n\nconst user = new User();\nuser.name = 'آنا'; // يتم استدعاء set\nconsole.log(user.name); // يتم استدعاء get → 'آنا'\nuser.name = ''; // محاولة تعيين اسم فارغ → رسالة في الكونسول\n\nلاحظ أن الخاصية _name بالبادئة _ هي حقل خاص. الوصول المباشر إليها من الخارج غير ممكن. بدلاً من ذلك، نستخدم الأكسيسور العام name.
لماذا نحتاج الجيتّرات والستّرات؟ 3 أسباب رئيسية
\n\n1. التحقق من صحة البيانات
\nالستّر هو المكان المثالي للتحقق من القيم المدخلة. يمكنك استبعاد البيانات غير الصحيحة قبل أن تصل إلى الكائن.
\n\nclass Temperature {\n private _celsius: number = 0;\n\n get celsius(): number {\n return this._celsius;\n }\n\n set celsius(value: number) {\n if (value < -273.15) {\n throw new Error('درجة حرارة أقل من الصفر المطلق غير ممكنة');\n }\n this._celsius = value;\n }\n}\n\n2. الخصائص المحسوبة
\nيمكن للجيتّر أن يعيد قيمة غير مخزنة، بل نتيجة حسابات بناءً على بيانات أخرى.
\n\nclass Rectangle {\n constructor(public width: number, public height: number) {}\n\n get area(): number {\n return this.width * this.height;\n }\n\n get perimeter(): number {\n return 2 * (this.width + this.height);\n }\n}\n\nconst rect = new Rectangle(10, 5);\nconsole.log(rect.area); // 50 — تُحسب فورًا\n\n3. التغليف والتحكم في الوصول
\nيمكنك جعل الخاصية للقراءة فقط (جيتّر فقط) أو للكتابة فقط (ستّر فقط). هذا يساعد في الالتزام بمبادئ البرمجة كائنية التوجه.
\n\nclass Config {\n private _apiKey: string = 'secret123';\n\n get apiKey(): string {\n // يمكن إضافة تسجيل أو التحقق من الصلاحيات\n return this._apiKey;\n }\n // لم يتم تعريف ستّر — الخاصية للقراءة فقط\n}\n\nconst config = new Config();\nconsole.log(config.apiKey); // 'secret123'\n// config.apiKey = 'newKey'; // خطأ في الترجمة!\n\nأسئلة شائعة حول get و set في TypeScript
\n\nدعنا نناقش ما يسأل عنه المبتدئون (وليس فقط) غالبًا على Stack Overflow والمنتديات.
\n\nالسؤال 1: ما الفرق بين get/set والطرق العادية؟
\nالفرق الرئيسي هو صيغة الاستدعاء. تبدو الأكسيسورات كخصائص: بدون أقواس. هذا يجعل الكود أنظف وأكثر بديهية. قارن:
\n\n// مع الطرق\nuser.getName();\nuser.setName('بطرس');\n\n// مع الأكسيسورات\nuser.name;\nuser.name = 'بطرس';\n\nالفرق الثاني هو أن الأكسيسورات لا يمكن تحميلها بشكل زائد (overload). لكل أكسيسور توقيع واحد