0Pricing
TypeScript Academy · درس

أنماط عدم القابلية للتغيير العميقة

حقّقوا هياكل غير قابلة للكتابة بعمق باستخدام الأنواع التكرارية

أنماط عدم القابلية للتغيير العميقة درس مجاني في TypeScript Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في TypeScript Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة TypeScript Academy 4 دروس في المجموع.

عدم القابلية للتغيير السطحي والعميق

إن المعدِّل readonly سطحي: فهو يحمي المستوى الأعلى، وليس الكائنات المتداخلة. أما عدم القابلية للتغيير العميق فيعني أن كل مستوى، حتى أعمق مستوى، للقراءة فقط. ويتطلب تحقيق ذلك عملًا إضافيًا.

interface State { readonly user: { name: string }; }
const s: State = { user: { name: 'Ada' } };
// s.user = {...}; // Error (shallow protection)
s.user.name = 'Grace'; // allowed! nested field is mutable
console.log(s.user.name);

لماذا لا يكفي الحظر السطحي

في إدارة الحالة والبيانات المشتركة، يترك readonly السطحي الحقول المتداخلة مكشوفة. وقد يؤدي تغيير واحد لم يُنتبه إليه في عمق بنية ما إلى أخطاء دقيقة يصعب تتبعها.

interface Config { readonly db: { host: string; port: number }; }
const c: Config = { db: { host: 'localhost', port: 5432 } };
c.db.port = 9999; // mutates nested data despite readonly
console.log(c.db.port);

نوع DeepReadonly تكراري

يمكننا إنشاء نوع mapped يطبّق readonly بشكل تكراري. إذ يتنقل DeepReadonly<T> عبر كل خاصية، وإذا كانت الخاصية كائنًا، يعاود تطبيق العملية داخلها.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
console.log('DeepReadonly defined');

تطبيق DeepReadonly

يؤدي تغليف نوع ما باستخدام DeepReadonly إلى جعل الخصائص المتداخلة غير قابلة للتغيير أيضًا. والآن لا يمكن حتى إعادة إسناد s.user.name — إذ تمتد الحماية إلى أعمق مستوى.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
type State = DeepReadonly<{ user: { name: string } }>;
const s: State = { user: { name: 'Ada' } };
// s.user.name = 'x'; // Error now
console.log(s.user.name);

آلية عمل النوع mapped

يتكرر [K in keyof T] عبر كل مفتاح. وتعمل السابقة readonly على قفل كل مفتاح. أما الشرط T[K] extends object ? ... : T[K] فيقرر ما إذا كان سيواصل التكرار أو يتوقف عند قيمة بدائية.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
type Cfg = DeepReadonly<{ a: number; b: { c: string } }>;
const cfg: Cfg = { a: 1, b: { c: 'x' } };
console.log(cfg.a, cfg.b.c);

حماية وقت الترجمة فقط

يفرض DeepReadonly عدم القابلية للتغيير في نظام الأنواع، وليس وقت التشغيل. يظل كائن JavaScript قابلًا للتغيير من الناحية التقنية عبر مسارات غير مكتوبة بالأنواع — لكن شيفرتك المكتوبة بالأنواع محمية بالكامل.

// Type-level immutability does not freeze the runtime object.
// For runtime guarantees, combine it with Object.freeze.
console.log('Types guard your code; freeze guards runtime');

تجميد الكائنات المتداخلة وقت التشغيل

لتحقيق عدم القابلية للتغيير الفعلية وقت التشغيل، استخدم Object.freeze. فهو يمنع إضافة الخصائص أو إزالتها أو تغييرها — لكنه، مثل readonly، سطحي افتراضيًا.

const config = Object.freeze({ host: 'localhost', port: 8080 });
console.log(config.port);
// In strict mode, config.port = 1 throws; otherwise silently ignored.

مساعد التجميد العميق

للتجميد العميق وقت التشغيل، كرر العملية عبر الكائنات المتداخلة وجمّد كل كائن منها. ويتكامل ذلك طبيعيًا مع DeepReadonly على مستوى النوع لتحقيق عدم القابلية للتغيير من البداية إلى النهاية.

function deepFreeze<T>(obj: T): T {
  Object.values(obj as any).forEach((v) => {
    if (v && typeof v === 'object') deepFreeze(v);
  });
  return Object.freeze(obj);
}
const frozen = deepFreeze({ a: { b: 1 } });
console.log(frozen.a.b);

عدم القابلية للتغيير لحالة أكثر أمانًا

يُعد عدم القابلية للتغيير العميق أساس الحالة القابلة للتنبؤ. فعندما يتعذر تغيير البيانات في موضعها، يجب أن تنشئ التحديثات كائنات جديدة، مما يجعل التغييرات واضحة وسهلة التتبع.

const state = { count: 0, user: { name: 'Ada' } };
// Immutable update: create a new object instead of mutating
const next = { ...state, count: state.count + 1 };
console.log(state.count, next.count);

نمط التحديثات غير القابلة للتغيير

مع البيانات للقراءة فقط، تنشئ إصدارات جديدة باستخدام عامل الانتشار. ويكون كل تحديث كائنًا جديدًا، فتظل الحالات السابقة سليمة — وهذا قيّم للتراجع، وتصحيح الأخطاء عبر الزمن، واكتشاف التغييرات.

type State = { readonly items: readonly string[] };
const s: State = { items: ['a'] };
const updated: State = { items: [...s.items, 'b'] };
console.log(s.items, updated.items);

الجمع بين أمان النوع ووقت التشغيل

يستخدم النهج الأقوى كليهما: DeepReadonly لضمانات وقت الترجمة وdeepFreeze للفرض وقت التشغيل. ومعًا يجعلان التغيير غير المقصود شبه مستحيل.

function freeze<T>(obj: T): T {
  return Object.freeze(obj);
}
const settings = freeze({ theme: 'dark', version: 2 });
console.log(settings.theme, settings.version);

اختبار سريع

اختبر مدى فهمك لعدم القابلية للتغيير العميق.

مراجعة: عدم القابلية للتغيير العميق

لقد تعلمت أن:

  • إن readonly سطحي؛ إذ تظل الحقول المتداخلة قابلة للتغيير.
  • يطبّق النوع mapped التكراري DeepReadonly<T> readonly على كل مستوى.
  • إنه يعمل وقت الترجمة فقط؛ فاستخدم Object.freeze (بشكل عميق) للفرض وقت التشغيل.
  • يتيح عدم القابلية للتغيير حالة آمنة وقابلة للتنبؤ من خلال أنماط النسخ عند التحديث.

وبذلك يكتمل مسار أكاديمية TypeScript حول الأنواع المتقدمة وعدم القابلية للتغيير.

type State = { readonly value: number };
const s: State = { value: 1 };
const next: State = { ...s, value: s.value + 1 };
console.log(s.value, next.value);

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

هل درس «أنماط عدم القابلية للتغيير العميقة» مجاني؟

نعم — نص درس «أنماط عدم القابلية للتغيير العميقة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة TypeScript Academy، انتقل إلى CoddyKit PRO. تتضمن دورة TypeScript Academy 4 دروس في المجموع.

ماذا ستتعلم في «أنماط عدم القابلية للتغيير العميقة»؟

حقّقوا هياكل غير قابلة للكتابة بعمق باستخدام الأنواع التكرارية تتمرن على TypeScript Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ TypeScript Academy؟

لا تُشترط خبرة سابقة. TypeScript Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «أنماط عدم القابلية للتغيير العميقة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس TypeScript Academy هذا؟

نعم. كل درس في TypeScript Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الخصائص readonly
  2. المصفوفات والصفائف readonly
  3. ReadonlyArray وReadonlyMap
  4. أنماط عدم القابلية للتغيير العميقة
← العودة إلى TypeScript Academy