Menu
flag Ar iconالعربيةdown icon

معدّلات الوصول في TypeScript: public وprivate وprotected

في TypeScript ثلاثة معدّلات وصول: public وprivate وprotected، إضافة إلى readonly. تعرّف على ما يسمح به كل منها، ولماذا private في TypeScript فحص وقت الترجمة بينما حقول #private في JavaScript تُفرض وقت التشغيل، وأيها تختار.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

في TypeScript ثلاثة معدّلات وصول لأعضاء الصنف: public (الافتراضي)، وprotected، وprivate. تتحكم في المواضع التي يمكن فيها استخدام العضو، ويبلّغ المترجم عن أي وصول من الموضع الخاطئ.

السطر الأخير خطأ ترجمة، لكن انظر إلى ما طبعه حين شُغّل رغم ذلك: 123-45-6789. هذه أهم حقيقة في هذه الصفحة، ويشرحها قسم private.

ما يسمح به كل معدّل

المعدّلداخل الصنففي صنف فرعيمن الخارجيُفرض وقت التشغيل
public (الافتراضي)نعمنعمنعملا شيء يُفرض
protectedنعمنعملالا
privateنعملالالا
#name (JavaScript)نعملالانعم
readonlyقراءة، وكتابة في دالة البناءقراءةقراءةلا

تعمل المعدّلات على الحقول والتوابع والـ getters والـ setters ودوال البناء وخصائص المعاملات (constructor(private id: string)). كتابة public اختيارية، وكثير من المشاريع تحذفها.

private فحص وقت الترجمة

تحذف TypeScript المعدّل private مع بقية الأنواع. الصنف المترجم يحتوي على خاصية عادية، فيراها كل ما لا يمر عبر فاحص الأنواع: مستدعو JavaScript العاديون، وJSON.stringify، وObject.keys، بل وصيغة الأقواس المربعة في TypeScript نفسها، المسموحة كمخرج مقصود.

هذا مناسب للغرض الذي وُجد له private: إخبار المطورين الآخرين (ومحررك) بأن العضو تفصيل تنفيذي. إنه ليس حدًا أمنيًا، وسينتهي الحقل private في السجلات واستجابات JSON ما لم تحذفه بنفسك.

حقول #private: تُفرض وقت التشغيل

لـ JavaScript حقولها الخاصة، وتُكتب بـ #. يفرضها المحرك: خارج الصنف لا تكون obj.#field صيغة صالحة أصلًا، ولا يظهر الحقل في Object.keys ولا JSON.stringify ولا console.log.

كتابة s.#token خارج الصنف هي الخطأ TS18013 (Property '#token' is not accessible outside class 'Session' because it has a private identifier)، وعلى خلاف حالة private لا توجد طريقة للالتفاف عليها وقت التشغيل أيضًا. راجع الحقول الخاصة في JavaScript لقواعد وقت التشغيل.

private مقابل #private: أيهما تستخدم

private x#x
يفحصهالمترجممحرك JavaScript
مرئي لـ Object.keys / JSON.stringifyنعملا
الوصول بالأقواس obj["x"]مسموحغير ممكن
يمكن للصنف الفرعي تعريف x خاص بهلا، يتعارضنعم، لكل صنف #x خاص به
يُنسخ بالنشر { ...obj }نعملا
الصيغة على التوابعprivate helper()#helper()

استخدم #private حين يجب أن تبقى البيانات خاصة وقت التشغيل (الرموز المميزة، والحالة الداخلية التي لا يجوز لمستخدمي مكتبة لمسها)، أو حين تريد أن يتجاهلها JSON.stringify. واستخدم private حين يكون الهدف مجرد واجهة عامة نظيفة، أو حين يحتاج إطار عمل إلى قراءة الحقل بالانعكاس (reflection)، أو لمطابقة الأسلوب القائم في المشروع. لا تجمع بينهما: private #x هو الخطأ TS18010 (An accessibility modifier cannot be used with a private identifier).

protected والأصناف الفرعية

العضو protected متاح داخل الصنف وفي أي صنف يرث منه، لكن ليس على النسخ من الخارج.

قاعدة واحدة تفاجئ الناس. داخل Polygon يمكنك قراءة sides على this أو على Polygon آخر، لكن ليس على Shape عادي يُمرَّر كمعامل: other.sides حيث other: Shape هو الخطأ TS2446 (Property 'sides' is protected and only accessible through an instance of class 'Polygon'. This is an instance of class 'Shape'.). لا يصل الصنف الفرعي إلى الأعضاء المحمية إلا في الكائنات التي تنتمي إلى فرعه من الهرمية.

يمكن للصنف الفرعي أن يجعل عضوًا محميًا عامًا بإعادة تعريفه، لكنه لا يستطيع أن يجعل عضوًا عامًا محميًا أو خاصًا. أعد تعريفه بقيمة ابتدائية (public override sides = 6) أو كنوع فقط (declare public sides: number). أما public sides: number; المجرد فيُرفض بالخطأين TS2564 وTS2612، لأنه مع حقول الأصناف الحديثة سيعيد ضبط القيمة الموروثة إلى undefined بعد عودة super().

readonly

يمنع readonly إعادة الإسناد بعد الإنشاء. يمكن ضبط الحقل في تعريفه أو في دالة البناء؛ وأي إسناد لاحق هو الخطأ TS2540.

يظهر هنا حدّان. readonly سطحي: لا يمكن استبدال المصفوفة، لكن محتواها يمكن أن يتغير (اجعل نوعها readonly string[] لمنع push). ومثل private يختفي وقت التشغيل، فالإسناد الذي رفضه المترجم نُفّذ رغم ذلك. للقيمة التي يجب ألا تتغير وقت التشغيل استخدم Object.freeze أو getter بلا setter. تشرح صفحة readonly النوع Readonly<T> والمصفوفات للقراءة فقط.

يجتمع readonly مع معدّلات الوصول: private readonly cache = new Map<string, number>() نمط شائع لحقل داخلي لا يُعاد إسناده أبدًا.

أخطاء شائعة

  • معاملة private كأداة أمان. يختفي وقت التشغيل. استخدم #field للبيانات التي يجب أن تبقى مخفية، ولا ترسل أبدًا كائنًا يحمل أسرارًا مباشرة إلى JSON.stringify.
  • كتابة public في كل مكان. إنه الافتراضي؛ وإضافته لا تغيّر شيئًا.
  • استخدام protected لكل ما هو «داخلي». إن لم يحتجه أي صنف فرعي، فإن private يُبقي الواجهة أصغر.
  • توقّع أن يجمّد readonly البيانات المتداخلة. إنه يمنع فقط إعادة إسناد الخاصية نفسها.

الأسئلة الشائعة

ما هي معدّلات الوصول في TypeScript؟

public (الافتراضي: متاح في كل مكان)، وprotected (داخل الصنف وأصنافه الفرعية)، وprivate (داخل الصنف فقط). أما readonly فمعدّل مستقل يمنع إعادة الإسناد بعد دالة البناء، ويمكن جمعه مع أي من الثلاثة.

ما الفرق بين private و#private في TypeScript؟

private يفحصه المترجم فقط؛ فيحتوي JavaScript الناتج على خاصية عادية، وتراها obj["secret"] وJSON.stringify وObject.keys. أما #secret فحقل خاص في JavaScript: يفرضه وقت التشغيل، ولا يستطيع الكود خارج الصنف قراءته إطلاقًا.

هل private في TypeScript خاص فعلًا؟

وقت الترجمة فقط. بعد الترجمة يصبح الحقل خاصية عادية يستطيع أي كود JavaScript قراءتها. بل إن TypeScript تسمح بالوصول بالأقواس المربعة (obj["field"]) إلى الأعضاء الخاصة كمخرج مقصود. استخدم #field حين يجب أن تصمد الخصوصية وقت التشغيل.

ما الفرق بين protected وprivate في TypeScript؟

العضو private مرئي فقط داخل الصنف الذي يعرّفه. أما العضو protected فمرئي أيضًا داخل الأصناف الفرعية. ولا يمكن الوصول إلى أي منهما على نسخة من خارج هرمية الأصناف.

هل يمكن تغيير خصائص readonly في TypeScript؟

يمكن إسناد خاصية readonly في تعريفها أو في دالة البناء، ولا مكان غير ذلك (وإلا فالخطأ TS2540). وهي سطحية: لا يمكن استبدال readonly tags: string[]، لكن tags.push() لا تزال تعمل. استخدم readonly string[] لمنع ذلك أيضًا.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن