التحليل الثابت

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

ESLint

ESLint هي أداة يمكنها تقديم ملاحظات حول المشكلات المحتملة في قاعدة التعليمات البرمجية لديك. قد تكون هذه المشاكل آمنة، ولكنها أخطاء أو سلوك غير عادي في حد ذاتها. يتيح لك ESLint تطبيق عدد من القواعد التي يتم التحقّق منها على قاعدة الرموز، بما في ذلك العديد من القواعد في المجموعة "المقترَحة".

من الأمثلة الجيدة على قاعدة ESLint قاعدة no-un-unsafe-Final. ويمنعك ذلك من كتابة عبارات تعدّل مسار التحكّم في برنامجك داخل مجموعة finally. هذه قاعدة رائعة، لأن القيام بذلك هو طريقة غير عادية لكتابة JavaScript قد يصعب اتباعها. ومع ذلك، فإنه أيضًا شيء ينبغي أن تكون عملية مراجعة التعليمات البرمجية السليمة قادرة على اكتشافه.

  try {
    const result = await complexFetchFromNetwork();
    if (!result.ok) {
      throw new Error("failed to fetch");
    }
  } finally {
    // warning - this will 'overrule' the previous exception!
    return false;
  }

وعلى هذا النحو، لا يُعد ESLint بديلاً لعملية مراجعة سليمة (ودليل أسلوب يحدد الشكل الذي يجب أن تبدو عليه قاعدة الرموز)، لأنه لن يلتقط كل النهج غير التقليدي الذي قد يحاول المطوّر تقديمه في قاعدة الرموز البرمجية الخاصة بك. يتضمن دليل الممارسات الهندسية من Google قسمًا قصيرًا حول "التبسيط".

يتيح لك ESLint مخالفة قاعدة وإضافة تعليق توضيحي على التعليمة البرمجية "مسموح به". على سبيل المثال، يمكنك السماح بالمنطق السابق من خلال التعليق التوضيحي عليه على النحو التالي:

  finally {
    // eslint-disable-next-line no-unsafe-finally
    return false;
  }

إذا وجدت نفسك تخالف إحدى القواعد باستمرار، ننصحك بإيقافها. تشجعك هذه الأدوات على كتابة التعليمات البرمجية بطريقة معينة، ولكن قد يتم استخدام فريقك لكتابة التعليمات البرمجية بطريقة مختلفة ويكون على دراية بمخاطر هذا النهج.

أخيرًا، قد يؤدي تفعيل أدوات التحليل الثابتة على قاعدة رموز برمجية كبيرة إلى حدوث الكثير من التشويش غير المفيد (والعمل المشغول لإعادة الهيكلة) بدلاً من التعليمات البرمجية التي تعمل جيدًا. لذلك من الأسهل تفعليها في وقت مبكر من دورة حياة المشروع.

مكوّنات ESLint الإضافية المتوافقة مع المتصفِّح

يمكنك إضافة مكوِّن إضافي إلى ESLint، حيث يُظهر استخدام واجهات برمجة التطبيقات غير المتوافقة على نطاق واسع أو غير المتوافقة مع قائمة المتصفِّح المستهدف. يمكن أن تحذّرك حزمة eslint-plugin-compat في حال عدم توفُّر واجهة برمجة تطبيقات للمستخدمين، وبالتالي لن تحتاج إلى تتبّع البيانات بنفسك باستمرار.

التحقق من النوع للتحليل الثابت

عند تعلّم لغة JavaScript، عادةً ما يفهم المطوّرون الجدد فكرة أنّ هذه اللغة مكتوبة بشكل ضعيف. أي، من الممكن الإعلان عن متغير كنوع واحد، ثم استخدام نفس الموقع لشيء مختلف تمامًا. وهذا يشبه لغة Python ولغات البرمجة النصية الأخرى، ولكن على عكس اللغات المجمّعة مثل C/C++ وRst.

قد يكون هذا النوع من اللغة جيدًا للبدء - ويمكن القول إنها هذه البساطة التي جعلت JavaScript شائعًا جدًا - ولكنها غالبًا ما تكون نقطة إخفاق لبعض قواعد التعليمات البرمجية أو على الأقل شيء يجعل حدوث أخطاء مربكة. على سبيل المثال، عند تمرير number حيث كان من المتوقَّع أن يكون string أو نوع كائن، يمكن لهذه القيمة المكتوبة بشكل غير صحيح أن تنتشر في مكتبات مختلفة قبل أن تتسبب في حدوث خطأ TypeError.

TypeScript

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

للاطّلاع على مثال سريع، إليك الرمز التالي الذي من المتوقع أن يتم تلقّيه معاودة الاتصال لقبول اسم string وعمر number:

const callback = (name: string, age: string): void => {
  console.info(name, 'is now', age, 'years old!');
};
onBirthday(callback);

يؤدي إلى حدوث الخطأ التالي عند تشغيله من خلال TypeScript، أو حتى عند تمرير مؤشر الماوس فوقه في بيئة تطوير برامج (IDE):

bad.ts:4:12 - error TS2345: Argument of type '(name: string, age: string) => void' is not assignable to parameter of type '(name: string, age: number) => void'.
  Types of parameters 'age' and 'age' are incompatible.
    Type 'number' is not assignable to type 'string'.

4 onBirthday(callback);
             ~~~~~~~~

Found 1 error in bad.ts:4
الرمز من المثال السابق، المعروض في بيئة التطوير المتكاملة (IDE)، مع رسالة الخطأ المعروضة في نافذة منبثقة.
VSCode تشير إلى أنّك مررت بنوع غير صحيح.

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

يتمثل الجزء الأكثر صعوبة في استخدام TypeScript في إعداده بشكلٍ صحيح. يحتاج كل مشروع إلى ملف tsconfig.json تتم قراءته بواسطة أداة سطر الأوامر tsc بشكل أساسي، مثل VSCode، بالإضافة إلى العديد من أدوات الإنشاء وأدوات الإنشاء الأخرى، بما في ذلك Vitest. يحتوي هذا الملف على مئات الخيارات والعلامات، ويمكنك العثور على بعض الموارد الجيدة لإعداده هنا:

نصائح عامة حول TypeScript

عند إعداد TypeScript واستخدامها من خلال ملف tsconfig.json، يُرجى مراعاة ما يلي:

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

تتضمن TypeScript أي

العلامة noImplicitAny هي أحد أكثر خيارات الإعداد فعالية ومكافأة في TypeScript. ومع ذلك، غالبًا ما يكون ذلك هو الأصعب أيضًا، خاصةً إذا كان لديك قاعدة رموز برمجية كبيرة. (يتم تفعيل العلامة noImplicitAny تلقائيًا إذا كنت في وضع strict، ولكن ليس في غير ذلك).

ستجعل هذه العلامة هذه الدالة تعرض خطأ:

export function fibonacci(n) {
  if (n <= 1) {
    return 0;
  } else if (n === 2) {
    return 1;
  }
  return fibonacci(n - 1) + fibonacci(n - 2);
}

ومع أنّه من الواضح أنّ n يجب أن يكون رقمًا، كقارئ، لا يمكن لـTypeScript تأكيد ذلك بثقة. إذا كنت تستخدم VSCode، فإن التمرير فوق الدالة سيصفها على النحو التالي:

function fibonacci(n: any): any

سيتمكّن المتصلون بهذه الدالة من تمرير قيمة من النوع any (نوع يسمح بأي نوع آخر)، وليس فقط number. يتيح لك تفعيل علامة noImplicitAny حماية هذا النوع من الرموز أثناء عملية التطوير، بدون الحاجة إلى إجراء اختبارات شاملة لمنطق النشاط التجاري للرمز البرمجي الذي يجتاز أنواع البيانات الخاطئة في أماكن محدّدة.

الحل البسيط هنا هو وضع علامة على كل من الوسيطة n ونوع الرجوع fibonacci على أنّها number.

لا تمنعك العلامة noImplicitAny من كتابة any واضحًا في قاعدة الرموز. لا يزال بإمكانك كتابة دالة تقبل أو تعرض النوع any. إنه يضمن فقط لك تسمية كل متغير بنوع.