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

null و undefined في TypeScript: الفحص و?. و??

مع strictNullChecks يصبح null وundefined نوعين منفصلين تجبرك TypeScript على معالجتهما. تعرّف على طرق فحصهما، وoptional chaining (?.)، وعلامة الاستفهام المزدوجة (??) و??=، والفرق بين الخاصية الاختيارية و| undefined.

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

مع تفعيل strictNullChecks (وهو جزء من strict)، يكون null وundefined نوعين قائمين بذاتهما. لا يمكن أبدًا أن يكون string هو null؛ والقيمة التي قد تغيب يجب أن تصرّح بذلك في نوعها، مثل string | null، وتجبرك TypeScript على معالجة هذه الحالة قبل استخدام القيمة.

بعد الفحص يعرف المترجم أن name من نوع string، فيُسمح بـ .split. تغطي بقية هذه الصفحة طرق إجراء هذا الفحص والعوامل التي تختصره.

strictNullChecks وأخطاء «قد يكون null»

عندما يتضمن النوع null أو undefined، ترفض TypeScript استخدام القيمة كأنها موجودة دائمًا:

index.ts(3,22): error TS18047: 'name' is possibly 'null'.

نسخة undefined هي TS18048، 'x' is possibly 'undefined'. دون strictNullChecks يُسمح بـ null وundefined في كل نوع ويُترجم هذا الكود، ثم ينهار وقت التشغيل بـ TypeError في أول مرة تكون فيها name هي null. هذه الفئة من الأخطاء هي السبب الرئيسي لإبقاء strict مفعّلًا.

من أين تأتي هذه الأنواع في الكود اليومي:

المصدرالنوع
arr.find(...)T | undefined
map.get(key)V | undefined
الخاصية الاختيارية p?: TT | undefined عند القراءة
المعامل الاختياري x?: TT | undefined داخل الدالة
str.match(re)RegExpMatchArray | null
JSON.parse(text)any، فلا يُفحص شيء

فحص null و undefined

كل فحص أدناه يضيّق النوع داخل الكتلة. اختر الفحص الذي يطابق ما تريد استبعاده.

الفحصيحذف من النوع
x !== undefinedundefined
x !== nullnull
x != nullnull وundefined
typeof x !== "undefined"undefined
if (x)null وundefined، ويتخطى أيضًا القيم 0 و"" وfalse وNaN

== null هو الموضع الوحيد الذي تكون فيه المساواة المرنة أسلوبًا مألوفًا: تكون صحيحة مع null وundefined بالضبط، ولا شيء غيرهما. أما فحص القيمة المنطقية فكان سيعامل النص الفارغ كقيمة غائبة، وهذا خطأ في أحيان كثيرة.

Optional Chaining: ?.

يقرأ a?.b الخاصية b إذا لم تكن a هي null أو undefined، وإلا يتوقف ويعيد undefined. ويعمل العامل نفسه مع الفهارس، a?.[i]، ومع الاستدعاءات، fn?.().

نوع bob.address?.city هو string | undefined: يضيف optional chaining النوع undefined إلى الناتج، لذلك تقرنه عادة بـ ??. والسلوك وقت التشغيل JavaScript عادية؛ انظر optional chaining لتفاصيل الاختصار (short-circuiting).

علامة الاستفهام المزدوجة: ??

يعيد a ?? b القيمة a إلا إذا كانت null أو undefined، فيعيد عندها b. إنه يحل محل الأسلوب الأقدم a || b، الذي يستبعد أيضًا 0 و"" وfalse وNaN:

القيمة في الطرف الأولleft || "d"left ?? "d"
null"d""d"
undefined"d""d"
0"d"0
"""d"""
false"d"false
NaN"d"NaN

على مستوى الأنواع، يحذف ?? النوعين null وundefined من الطرف الأول ويجمع الباقي في union مع الطرف الثاني، ولهذا يمكن إسناد scores.get("Linus") ?? 0 إلى number.

الإسناد عند الفراغ: ??=

يسند a ??= b القيمة b إلى a فقط عندما تكون a هي null أو undefined. وشقيقاه ||= و&&= يسندان عندما يكون الطرف الأول خاطئًا أو صحيحًا منطقيًا.

تبقى retries: 0 مع ??=، بينما يُستبدل label الفارغ مع ||=. بعد opts.retries ??= 3 تضيّق TypeScript الخاصية opts.retries إلى number في بقية الدالة.

الخصائص الاختيارية مقابل | undefined

تُقرأ nickname?: string وnickname: string | undefined بالطريقة نفسها، لكنهما تختلفان في ما إذا كان يجب وجود المفتاح:

استخدم ? عندما يمكن للمستدعين ترك الخاصية، و| undefined عندما تريد أن يمررها كل مستدعٍ صراحة، حتى لو كانت قيمتها undefined. والخيار exactOptionalPropertyTypes (ليس جزءًا من strict) يشدد ? أكثر: عندها ترفض nickname?: string القيمة { nickname: undefined } ولا تقبل إلا مفتاحًا غائبًا أو نصًا. والمعاملات الاختيارية للدوال (x?: number) تتصرف مثل الخصائص الاختيارية: داخل الدالة يكون x من نوع number | undefined.

null أم undefined: أيهما تستخدم

لا تفرض TypeScript اختيارًا، لكن خلط الاثنين في قاعدة كود واحدة يعني أن كل فحص يجب أن يعالج حالتين. عرف شائع:

  • استخدم undefined (والخصائص الاختيارية) لـ «غير مضبوط» في أنواعك الخاصة. هذا ما تنتجه JavaScript افتراضيًا: الخصائص الغائبة، والوسائط المحذوفة، وحالات عدم العثور في find وMap.get.
  • اقبل null حيث تعطيك إياه واجهة برمجية: لا يوجد undefined في JSON، وتعيد String.prototype.match وكثير من دوال DOM القيمة null.
  • افحص بـ == null عندما قد تكون القيمة أيًا منهما.

فهرسة المصفوفات هي الثغرة الوحيدة: users[5] نوعه نوع العنصر حتى عندما يكون الفهرس خارج النطاق. يضيف الخيار noUncheckedIndexedAccess (ليس جزءًا من strict) | undefined إلى كل وصول بالفهرس حتى يكتشف المترجم ذلك أيضًا.

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

ماذا تعني علامة الاستفهام المزدوجة في TypeScript؟

a ?? b هو عامل nullish coalescing من JavaScript. يعيد a إلا إذا كانت a هي null أو undefined، فيعيد عندها b. وعلى خلاف || يحتفظ بالقيم الأخرى التي تُعد خاطئة منطقيًا مثل 0 و"" وfalse. تحذف TypeScript النوعين null وundefined من نوع الطرف الأول، فعندما يكون a من نوع string | undefined يكون a ?? "x" من نوع string.

كيف أتحقق من أن قيمة undefined في TypeScript؟

قارنها: if (value !== undefined) { ... }. تضيّق TypeScript النوع داخل الكتلة. ولاستبعاد null وundefined معًا بفحص واحد، استخدم value != null (المساواة المرنة)، وهو الموضع الوحيد الذي يكون فيه ==/!= أسلوبًا مألوفًا. وفحص القيمة المنطقية (if (value)) يضيّق أيضًا لكنه يتخطى 0 و"" وfalse.

ماذا تعني الرسالة "Object is possibly undefined"؟

الأخطاء TS18048 ('x' is possibly 'undefined') وTS18047 ('x' is possibly 'null')، أو TS2532 (Object is possibly 'undefined') عندما لا يكون للقيمة اسم بسيط، كما في getUser().address.city، تأتي من strictNullChecks: النوع يتضمن undefined أو null، والكود يستخدم القيمة كأنها لا يمكن أن تكون كذلك. افحصها أولًا، أو استخدم optional chaining (x?.name)، أو وفّر قيمة بديلة بـ ??، أو غيّر النوع إذا كانت القيمة فعلًا لا يمكن أن تغيب.

ما الفرق بين null و undefined في TypeScript؟

هما نوعان منفصلان لكل منهما قيمة واحدة. تستخدم JavaScript القيمة undefined لما لم يُضبط قط (خاصية غائبة، أو وسيط محذوف، أو Map.get على مفتاح غير موجود)، وتستخدم الواجهات البرمجية null لـ «لا قيمة» المقصودة (JSON وكثير من دوال DOM). تتتبعهما TypeScript منفصلين، لذلك لا يقبل string | null القيمة undefined. تختار قواعد كود كثيرة undefined لكودها الخاص ولا تقبل null إلا عند الحدود.

هل الخاصية الاختيارية هي نفسها | undefined؟

ليس تمامًا. name?: string تعني أن الخاصية قد تغيب كليًا، وقراءتها تعطي string | undefined. أما name: string | undefined فتعني أن الخاصية يجب أن تكون موجودة، حتى لو كانت قيمتها undefined. ومع تفعيل exactOptionalPropertyTypes تتوقف name?: string أيضًا عن قبول قيمة undefined صريحة.

Coddy programming languages illustration

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

ابدأ الآن