تايب سكريبت: الوصول إلى عنصر في المصفوفة لا يأخذ في الاعتبار احتمال القيم المرجعة غير المعرفة


الكود التالي يمر بفاحص نوع Typescript (الإصدار 2.9.1)، لكنه يرمي TypeError أثناء التشغيل.

interface Item { id: string }
const list: Item[] = [{ id: 'a' }, { id: 'b' }];
const item = list[3]; // type: Item
const itemId = item.id; // type: string

نظرًا لأن الوصول إلى عنصر في مصفوفة نوعية يمكن أن يُرجع دائمًا undefined، أليس من المفترض أن يكون العنصر item: Item | undefined، مما سيجبرك على إجراء فحص للقيم الفارغة؟

والأمر الأكثر إثارة للدهشة بالنسبة لي، هو أن ما يلي أيضًا يجتاز فحص النوع:

const item2: Item | undefined = list[3];
const item2Id = item2.id;

على الرغم من أن تحويل القيمة المرجعة يفشل بنجاح في فحص النوع:

const item3 = list[3] as Item | undefined;
const item3Id = item3.id; // [ts] Object is possibly 'undefined'.

إنشاء دالة وصول ذات نوع محدد صراحةً يلتقط أيضًا حالة غير المعرفة، لكنه يضيف عبئًا غير ضروري:

const getItem1 = (index: number, items: Item[]): Item | undefined => items[index];
const item3 = getItem1(3, list);
const item3Id = item3 && item3.id;

هل هذا قيد معروف في تايب سكريبت؟ هل هناك أنماط أو مكتبات موصى بها للتعامل مع هذه الحالة؟

الإجابة رقم 1

تحديث لـ TS 4.1:

قدم TypeScript 4.1 علم مترجم --noUncheckedIndexedAccess الذي ينفذ الاقتراح في microsoft/TypeScript#13778 لأخذ undefined في الاعتبار بهذه الطريقة. لاحظ أن الميزة لن تكون مفعلة كجزء من مجموعة خيارات المترجم --strict ويتم تسميتها "تواقيع الفهرس الدقيقة" لأنها ستقوم في النهاية بالشكوى بشأن احتمال وجود undefined في مواقف قد لا يتوقعها أو يرغب بها المبرمجون.

لتحديث tsconfig.json: { "noUncheckedIndexedAccess": true }


إجابة ما قبل TS4.1:

لقد اكتشفت أن توقيعات الفهرس لا تضيف | undefined إلى نوع العنصر بالطريقة التي تفعلها الخصائص الاختيارية. تم اقتراح ذلك في microsoft/TypeScript#13778 لإنشاء خيار للمترجم يقوم بذلك. يمكنك قراءة التعليقات في هذا الاقتراح؛ فهي مرتبطة بمشاكل أخرى، ولكن الإجماع هو أن معدل الإيجابيات الكاذبة العالي سيجعلها عديمة الفائدة تقريبًا.

ويُذكر أيضًا أن لديك القدرة على إضافة | undefined إلى نوع العنصر يدويًا:

const list: (Item | undefined)[] = [{ id: 'a' }, { id: 'b' }];

والذي سيتصرف كما تتوقع، دون التأثير على اللغة بأكملها.

الإجابة رقم 2

هذا هو السلوك المقصود. انظر هذه المشكلة على مستودع GitHub الخاص بـ TypeScript لمناقشة طويلة

والأمر الأكثر إثارة للدهشة بالنسبة لي، هو أن ما يلي أيضًا يجتاز فحص النوع:

لديك strictNullChecks مغلق؛ حاول تشغيله.