FlowingDev

شرح TypeScript: كيف حصلت JavaScript على حزام الأمان والوسائد الهوائية

تعلم لماذا TypeScript هي JavaScript بقدرات خارقة، حيث تضيف أنواعًا ثابتة (static types) لاكتشاف الأخطاء مبكرًا وجعل الكود أكثر قوة ووضوحًا.

جرّب الأداة: ملعب TypeScript

في جملة واحدة

TypeScript هي لغة برمجة مبنية على JavaScript، تضيف "نظام أنواع" (type system)، والذي يشبه مدققًا نحويًا لبياناتك يلتقط فئة كبيرة من الأخطاء حتى قبل تشغيل الكود.

المشكلة التي تحلها

تخيل الأيام الأولى للويب. وُلدت JavaScript على عجل (في 10 أيام فقط، وهي قصة مشهورة) لجعل صفحات الويب أكثر تفاعلية. كانت مصممة للسكربتات الصغيرة: تحريك زر، التحقق من صحة حقل إدخال. كانت ديناميكية ومرنة ومتسامحة. هذا التسامح كان أعظم نقاط قوتها، ومع نمو الويب، أصبح أعظم نقاط ضعفها.

في JavaScript العادية (vanilla JavaScript)، يمكن للمتغير أن يكون أي شيء يريده. يمكن أن يبدأ كرقم، ثم يُعاد تعيينه ليصبح نصًا (string)، ثم يصبح كائنًا (object)، وفي النهاية ينتهي به الأمر كـ undefined.

let myThing = 123;         // myThing هو رقم
myThing = "Hello world"; // الآن هو نص
myThing = { id: 456 };   // والآن، هو كائن

لسكربت من 20 سطرًا، هذا مقبول. ولكن لتطبيق من 200,000 سطر يغذي خدمة ضخمة مثل خرائط Google أو Figma، فهذه وصفة للفوضى. كان المطورون يمررون النوع الخاطئ من البيانات إلى دالة، مما يؤدي إلى الخطأ الشهير undefined is not a function في وقت التشغيل (runtime) — أي أمام المستخدم الحقيقي. إعادة هيكلة الكود (Refactoring) كانت كابوسًا؛ غيّر دالة واحدة، وقد تكسر عشرة أجزاء أخرى من التطبيق دون أن تعرف حتى فوات الأوان.

واجهت Microsoft هذه المشكلة بالضبط في مشاريعها الكبيرة المعتمدة على JavaScript، فقررت فرض بعض النظام على هذه الفوضى. في عام 2012، أطلقوا TypeScript. لم يكن الهدف استبدال JavaScript، بل تعزيزها. كان من المفترض أن تضيف طبقة من الأمان والقدرة على التنبؤ فوق اللغة التي يعرفها الجميع بالفعل، مما يجعلها مناسبة للتطبيقات الضخمة والمعقدة التي يتطلبها الويب الحديث.

كيف تعمل تحت الغطاء

سحر TypeScript يحدث قبل أن يصل الكود إلى المتصفح. متصفحك لا يشغل TypeScript؛ بل يشغل JavaScript القديمة والطيبة. وظيفة TypeScript هي أن تعمل كمدقق كود (linter) فائق الذكاء واستباقي أثناء عملية التطوير.

### TypeScript هي مجموعة شاملة (Superset) لـ JavaScript

هذا هو أهم مفهوم يجب استيعابه. "Superset" يعني أن أي كود JavaScript صالح هو أيضًا كود TypeScript صالح. يمكنك أخذ ملف .js، وإعادة تسميته إلى .ts، وسيعمل. TypeScript ببساطة تضيف صيغة (syntax) جديدة فوق JavaScript.

الإضافة الأساسية هي تحديد الأنواع (type annotations). يمكنك الآن إخبار TypeScript بنوع البيانات التي تتوقعها.

// هذا كود JavaScript عادي
function getLength(someValue) {
  return someValue.length;
}

// هذا كود TypeScript
function getLengthTyped(someValue: string | any[]) {
  return someValue.length;
}

في نسخة JavaScript، إذا استدعيت getLength(42)، ستحصل على خطأ في وقت التشغيل لأن الرقم 42 لا يملك خاصية length. في نسخة TypeScript، محرر الكود والمترجم (compiler) سيصرخان في وجهك فورًا، ويخبرانك أن الرقم ليس مُعامِلاً (argument) صالحًا. يتم اكتشاف الخطأ حتى قبل حفظ الملف.

### عملية التحويل (Transpilation)

إذًا، إذا كانت المتصفحات لا تشغل TypeScript، فماذا يحدث لتلك الصيغة الإضافية؟ يتم مسحها في عملية تسمى transpilation.

الـ Transpilation هو نوع معين من الترجمة (compilation): تحويل من مصدر إلى مصدر. مترجم TypeScript (برنامج يسمى عادة tsc) يقرأ ملف .ts الخاص بك، ويتحقق من أخطاء الأنواع، ثم يُخرج ملف .js نظيفًا يمكن لأي متصفح فهمه.

إليك مثال قبل وبعد:

مدخلات TypeScript (ملف .ts)

function greet(person: string, date: Date): string {
  return `Hello ${person}, today is ${date.toDateString()}!`;
}

مخرجات JavaScript (ملف .js)

function greet(person, date) {
  return "Hello ".concat(person, ", today is ").concat(date.toDateString(), "!");
}

لاحظ كيف اختفت تحديدات الأنواع (: string، : Date) تمامًا. استخدمها الـ transpiler للتحقق من عملك ثم تخلص منها، تاركًا وراءه كود JavaScript قابلًا للتنفيذ. أي TypeScript playground يقوم بخطوة الـ transpilation هذه فورًا، مباشرة في متصفحك، حتى تتمكن من رؤية النتيجة في الوقت الفعلي.

### التحقق من الأنواع والاستدلال (Type Checking and Inference)

"عقل" TypeScript هو مدقق الأنواع (type-checker) الخاص بها. إنه الجزء الذي يحلل الكود الخاص بك ويتحقق من تطابق الأنواع. لكن ليس عليك دائمًا كتابة الأنواع بنفسك. TypeScript ذكية بما يكفي لاكتشافها في كثير من الأحيان من خلال عملية تسمى استدلال الأنواع (type inference).

let name = "Alice"; // تستدل TypeScript أن نوع `name` هو `string`
let age = 30;     // تستدل TypeScript أن نوع `age` هو `number`

// إذا حاولت فعل هذا، ستُظهر TypeScript خطأ:
// Type 'number' is not assignable to type 'string'.
name = 42;

لم تكن مضطرًا لكتابة let name: string = "Alice". رأت TypeScript أنك أسندت قيمة نصية واستدلت النوع بشكل صحيح. هذا يحافظ على كودك نظيفًا وقابلًا للقراءة مع منحك مزايا الأمان الكاملة. تحتاج فقط إلى إضافة أنواع صريحة عندما لا تستطيع TypeScript اكتشافها بنفسها، كما هو الحال مع معاملات الدوال (function parameters) أو المتغيرات الفارغة.

قصص من الواقع

### إعادة الهيكلة الكبرى (The Great Refactor)

كان المنتج الرئيسي لشركة ناشئة عبارة عن تطبيق JavaScript ضخم ومترابط (monolith). الفريق الأولي تحرك بسرعة و"كسر الأشياء"، والآن كان الفريق الجديد يدفع الثمن. كانوا بحاجة إلى إعادة هيكلة وحدة أساسية تتعامل مع أذونات المستخدم. المشكلة؟ لم يكن أحد متأكدًا من الشكل الذي يجب أن يكون عليه كائن user. هل يحتوي على user.isAdmin أم user.role === 'admin'؟ هل يأتي user.id كرقم أم كنص؟

كل تغيير كان مقامرة. قرروا إدخال TypeScript، ملفًا تلو الآخر. بدأوا بتعريف interface واضح لـ User:

interface User {
  id: number;
  email: string;
  role: 'admin' | 'editor' | 'viewer';
  lastLogin: Date;
}

فجأة، أضاءت بيئات التطوير المتكاملة (IDEs) الخاصة بهم مثل شجرة عيد الميلاد، مشيرة إلى كل مكان في الكود كانوا يستخدمون فيه كائن user بشكل غير صحيح. كانت هناك دالة تحاول الوصول إلى user.isAdmin، وهو غير موجود في الـ interface الجديد. وأخرى كانت تمرر ID كنص بدلاً من رقم. تحولت إعادة الهيكلة من لعبة تخمين مرعبة إلى عملية منهجية وموجهة.

الدرس: تحوّل TypeScript إعادة الهيكلة الفوضوية إلى مهمة هندسية آمنة ويمكن التنبؤ بها عن طريق إنشاء مصدر حقيقة واحد لهياكل بياناتك.

### خطأ الـ API الشبحي

كانت مطورة واجهة أمامية تبني لوحة تحكم تعرض بيانات من API تابع لطرف ثالث. وثائق الـ API ذكرت بوضوح أن سعر المنتج سيكون رقمًا، مثل 19.99. كتبت كود TypeScript الخاص بها وفقًا لذلك: interface Product { name: string; price: number; }.

عمل كودها بشكل رائع لأسابيع، حتى أبلغ أحد المستخدمين ذات يوم أن حساب "الإجمالي" يظهر NaN (ليس رقمًا). بعد ساعات من التصحيح، اكتشفت أنه لمنتج معين ونادر، كان الـ API يعيد السعر كنص: "19.99". في JavaScript، 10 + "19.99" ينتج عنه النص "1019.99"، مما أفسد حساباتها لاحقًا.

مع TypeScript، كان بإمكانها اكتشاف هذا من المصدر. من خلال التحقق من بيانات الـ API الواردة مقابل interface Product الخاص بها، كان عدم تطابق النوع سيطلق خطأ في اللحظة التي دخلت فيها البيانات السيئة إلى نظامها، وليس بعد عشر خطوات عندما حاولت إجراء عملية حسابية.

الدرس: تفرض TypeScript عقود البيانات، مما يجعل تطبيقك مرنًا في مواجهة التغييرات غير المتوقعة أو الأخطاء من واجهات برمجة التطبيقات (APIs) الخارجية.

أخطاء ومصائد شائعة

  • مخرج الهروب any: لدى TypeScript نوع خاص يسمى any، والذي يخبر المترجم (compiler) بشكل أساسي "ثق بي، أعرف ما أفعله" ويعطل كل عمليات التحقق من الأنواع لهذا المتغير. الإفراط في استخدام any يشبه شراء سيارة لميزات الأمان فيها ثم إيقاف تشغيلها جميعًا. إنه يبطل الغرض الكامل من استخدام TypeScript.
  • تجاهل أخطاء المترجم: أحيانًا، سترى خطًا أحمر متعرجًا من TypeScript، لكن كود JavaScript المحوّل يبدو أنه يعمل بشكل جيد. من المغري تجاهل الخطأ. هذه مصيدة. الخطأ هو علامة تحذير على وجود خطأ محتمل في وقت التشغيل. استمع إلى المترجم (compiler)؛ إنه يحاول مساعدتك.
  • محاربة نظام الأنواع: غالبًا ما يصاب المبتدئون بالإحباط عندما لا تسمح لهم TypeScript بفعل شيء ما. بدلاً من اكتشاف الطريقة الصحيحة لتحديد أنواع الكود، يحاولون خداع المترجم بتأكيدات النوع (as any) أو حلول أخرى. هذا يعني عادةً أن فهمهم لتدفق البيانات معيب، وأن المترجم يحدد المشكلة بشكل صحيح.
  • نسيان حزم @types: العديد من مكتبات JavaScript الشهيرة لا تتضمن تعريفات الأنواع الخاصة بها. يحافظ مجتمع TypeScript على مستودع ضخم لهذه التعريفات يسمى DefinitelyTyped. إذا كنت تستخدم مكتبة مثل Lodash أو Express، فغالبًا ما تحتاج إلى تثبيت حزمة @types/lodash المقابلة للحصول على الإكمال التلقائي والتحقق من الأنواع بشكل صحيح.

لماذا يجب أن تكون على رادارك

يجب أن تلجأ إلى TypeScript (أو على الأقل تفكر فيها) عندما:

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

باختصار، TypeScript هي المعيار الاحترافي لتطوير JavaScript الحديث. تعلمها هو استثمار يؤتي ثماره من خلال أخطاء أقل، وتعاون أسهل، وتجربة تطوير أكثر ثقة.

تعمق أكثر

  • TypeScript Official Website & Handbook: المصدر الرسمي. الكتيب هو دليل ممتاز وشامل.
  • TypeScript on Wikipedia: لإلقاء نظرة عامة على تاريخها وتصميمها وميزاتها.
  • MDN: Understanding TypeScript: وجهة نظر Mozilla حول كيفية تناسب TypeScript مع منظومة تطوير الويب.
  • DefinitelyTyped: مستودع جميع تعريفات الأنواع التابعة لجهات خارجية والتي تجعل منظومة TypeScript غنية جدًا.
  • TypeScript Deep Dive: كتاب إلكتروني رائع ومتعمق يغطي كل شيء من الأساسيات إلى المفاهيم المتقدمة.

كيف يفهم المحرر الشيفرة

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

CodeMirror & Lezer: MIT.

انتهينا من النظرية. حان وقت التطبيق — 100% في متصفحك.

جرّب الأداة: ملعب TypeScript