اذهب إلى المحتوى

مدخل إلى TypeScript


ابراهيم الخضور

TypeScript هي لغة برمجة مصممة لتنفيذ مشاريع JavaScript ضخمة، صممتها Microsoft. فقد بُنيت على سبيل المثال برنامج مثل Azure Management Portal الذي يبلغ عدد أسطر شيفرته 1.2 مليون، وبرنامج Visiual Studio Code الذي يبلغ عدد أسطر شيفرته 300 ألف باستخدام TypeScript. تقدم TypeScript ميزات عديدة لدعم مشاريع JavaScript الضخمة مثل أدوات تطوير أفضل وتحليل شيفرة ساكنة والتحقق من الأنواع عند الترجمة والتوثيق على مستوى الشيفرة.

المبدأ الرئيسي

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

تتألف TypeScript من ثلاثة أقسام منفصلة لكنها متكاملة مع بعضها:

  • اللغة
  • المصرِّف
  • خدمة اللغة

typescript_parts_01.png

تتألف اللغة من القواعد والكلمات المحجوزة (تعليمات) ومسجلات الأنواع Type annotations وتتشابه قواعدها مع مثيلاتها في JavaScript لكنها ليست متماثلة. وأكثر ما يتعامل معه المبرمجون من تلك الأقسام هي اللغة.

تنحصر مهمة المصرِّف في محو المعلومات المتعلقة بالنوع بالإضافة إلى التحويلات على الشيفرة. إذ تمكن عملية تحويل الشيفرة من نقل شيفرة TypeScript إلى شيفرة JavaScript قابلة للتنفيذ. يزيل المصرِّف كل ما يتعلق بالأنواع أثناء الترجمة، وبالتالي لا تميز هذه اللغة الأنواع بشكل فعلي قبل الترجمة.

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

ينفذ المصرِّف أيضًا عملية تحليل الشيفرة ما قبل التنفيذ. إذ يمكنه إظهار رسائل التحذير والخطأ إن كان هناك سبب لذلك، كما يمكن أن يُهيَّئ لتنفيذ مهام إضافية كضم الشيفرة الناتجة في ملف واحد.

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

الميزات الرئيسية للغة TypeScript

سنقدم تاليًا وصفًا لبعض ميزات اللغة TypeScript. والغاية من ذلك تزويدك بالأساسيات التي تقدمها اللغة وميزاتها، وذلك للتعامل مع الأفكار التي سنراها تباعًا في المنهاج.

مسجلات الأنواع

وهي طريقة خفيفة في TypeScript لتسجيل الأنواع التي نريد أن تمرر أو تعاد من الدوال أو أنواع المتغيرات. فلقد عرفنا في المثال التالي على سبيل المثال الدالة birthdayGreeter التي تقبل معاملين أحدهما من النوع string، والآخر من النوع number، وستعيد قيمة من النوع string.

const birthdayGreeter = (name: string, age: number): string => {
  return `Happy birthday ${name}, you are now ${age} years old!`;
};

const birthdayHero = "Jane User";
const age = 22;

console.log(birthdayGreeter(birthdayHero, 22));

نظام الخصائص المعرفة للأنواع

تستخدم اللغة TypeScript الخصائص المعرفة للأنواع structural typing. ويعتبر عنصران في هذا النظام متوافقان إن كان لكل ميزة في النوع الأول ميزة تماثلها تمامًا في النوع الثاني. ويعتبر النوعان متطابقان إذا كانا متوافقان مع بعضهما.

الاستدلال على النوع

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

لنتأمل على سبيل المثال الدالة Add:

const add = (a: number, b: number) => {
  /* تستخدم القيمة المعادة لتحديد نوع القيمة التي تعيدها الدالة*/
  return a + b;
}

يستدل المصرِّف على نوع القيمة المعادة للدالة بتعقب الشيفرة حتى الوصول إلى عبارة return. تعيد هذه العبارة مجموع المعاملين a وb. وكما نرى فكلا المعاملين من النوع number وهكذا سيستدل المصرِّف على أن القيمة التي تعيدها الدالة من النوع number.

وكمثال أكثر تعقيدًا، لنتأمل الشيفرة التالية (قد يكون المثال صعب الفهم قليلًا إن لم تكن قد استخدمت TypeScript مسبقًا. يمكنك تخطي هذا المثال حاليًا.)

type CallsFunction = (callback: (result: string) => any) => void;

const func: CallsFunction = (cb) => {
  cb('done');
  cb(1);
}

func((result) => {
  return result;
});

بداية نجد تصريحًا لاسم نوع مستعار type alias يدعى CallsFunction. يُمثل نوع دالة تقبل معاملًا واحدًا callback يمثِّل بدوره دالة تتلقى معاملًا من النوع "string" وتعيد قيمة من النوع any. وكما سنرى لاحقًا فالنوع any هو شكل من أشكال الحروف البديلة wildcards والتي يمكن أن تحل محل أي نوع.

بعد ذلك نعرّف الدالة func من النوع CallsFunction. يمكننا أن نستدل من نوع الدالة بأن معاملها الدالة cb ستقبل فقط معاملًا من النوع string.ولتوضيح ذلك سنورد مثالًا آخر تُستدعى فيه دالة كمعامل لكن بقيمة من النوع number، وسيسبب هذا الاستدعاء خطأً في TypeScript.

وأخيرًا نستدعي الدالة func بعد أن نمرر إليها الدالة التالية كمعامل:

(result) => {
  return result;
}

وعلى الرغم من عدم تحديد أنواع معاملات الدالة، يمكننا الاستدلال من سياق الاستدعاء أن المعامل result من النوع String.

إزالة الأنواع

تزيل TypeScript جميع الأنواع التي يبنيها نظام تحديد الأنواع أثناء الترجمة:

فلو كانت الشيفرة قبل الترجمة كالتالي:

let x: SomeType;

ستكون بعد الترجمة:

let x;

ويعني هذا عدم وجود أية معلومات عن الانواع أثناء التنفيذ، فلا يشير أي شيء على أن متغيرًا X على سبيل المثال قد عُرِّف أنه من نوع ما.

سيكون النقص في المعلومات عن الأنواع في زمن التنفيذ مفاجئًا للمبرمجين الذين اعتادوا على استخدام تعليمات التحقق Reflection وغيرها من أنظمة إظهار البيانات الوصفية.

هل علينا فعلا استعمال TypeScript؟

إن كنت من متابعي المنتديات البرمجية على الانترنت، ستجد تضاربًا في الآراء الموافقة والمعارضة لاستخدام هذه اللغة.

والحقيقة أنّ مدى حاجتك لاستخدام الدوال التي تقدمها TypeScript هي من يحدد وجهة نظرك. سنعرض على أية حال بعض الأسباب التي قد تجعل استخدام هذه اللغة مفيدًا.

أولًا: ستقدم ميزة التحقق من الأنواع وميزة التحليل الساكن للشيفرة (قبل الترجمة والتنفيذ). كما يمكننا أن نطلب قيمًا من نوع محدد وأن نتلقى تحذيرات من المصرِّف عندما نستخدم هذه القيم بشكل خاطئ. سيقلل هذا الأمر الأخطاء التي تحصل وقت التنفيذ، كما يمكن أن يقلل من عدد اختبارات الأجزاء unit tests التي يحتاجها التطبيق وخاصة ما يتعلق باختبارات الأنواع الصرفة pure types. ولا تحذرك عملية التحليل الساكن للشيفرة من الاستخدام الخاطئ للأنواع وحسب، بل تشير إلى الأخطاء الأخرى ككتابة اسم المتغير أو الدالة بشكل خاطئ أو استخدام المتغير خارج المجال المعرّف ضمنه.

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

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

ثالثًا: ستقدم بيئة عمل اللغة ميزة إكمال الشيفرة بشكل أفضل لأنها تعلم تمامًا نوع البيانات التي تعالجها.

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

ما الأمور التي لا يمكن للغة TypeScript أن توفرها لك؟

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

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

الأنواع غير المكتملة أو الخاطئة أو المفقودة التي مصدرها مكتبات خارجية

قد تجد في بعض المكتبات الخارجية أنواعًا غير معرّفة أو ذات تعريف خاطئ بشكل أو بآخر. والسبب المرجح أنّ المكتبة لم تُكتَب بلغة Typescript، وأنّ المبرمج الذي صرّح عن النوع يدويًا قد ارتكب خطأً. عليك في هذه الحال كتابة التصريح عن النوع يدويًا بنفسك. لكن هناك فرصة كبيرة أن يكون أحدهم قد فعل ذلك مسبقًا عوضًا عنك، تحقق من موقع DefinitelyTyped أو صفحة GitHub الخاصة بهذا الموقع أولًا. وهذان المصدران هما الأكثر شعبية لإيجاد ملفات تعريف الأنواع. في حال لم يحالفك الحظ، لابدّ أن تبدأ من الصفر بقراءة توثيق TypeScript بما يخص تعريف الأنواع.

قد يحتاج الاستدلال على النوع إلى بعض المساعدة

إن الاستدلال على النوع في هذه اللغة جيد جدًا لكنه ليس مثاليًا.

قد تعتقد أحيانًا أنك صرحت عن نوع بشكل مثالي، لكن المصرِّف يحذرك بشكل مستمر من أن خاصية محددة غير موجودة أو أنّ التصريح بهذه الطريقة غير مسموح. عليك في حالات كهذه مساعدة المصرِّف بتنفيذ عملية تحقق إضافية من النوع الذي صرحت عنه، وخاصة فيما يتعلق بالتحويل بين الأنواع type casting أو حاميات الأنواع type gaurds.

عند التحويل بين الأنواع أو عند استخدام الحاميات فأنت بذلك تضمن للمصرِّف أن القيمة التي وضعتها هي بالتأكيد من النوع المصرح عنه وهذا مصدر للأخطاء. ربما عليك الاطلاع على التوثيق فيما يتعلق بحاميات الأنواع أو مؤكدات الأنواع Type Assertions.

أخطاء الأنواع الغامضة

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

ترجمة -وبتصرف- للفصل Background and introduction من سلسلة Deep Dive Into Modern Web Development


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...