TypeScript هي JavaScript مع نظام أنواع ثابت مضاف فوقها. كل برنامج JavaScript هو صياغة TypeScript صحيحة؛ وتضيف TypeScript تعليقات الأنواع، ومترجمًا يفحصها قبل تشغيل الكود، وخطوة بناء تحذفها من جديد. وقت التشغيل لا يوجد إلا JavaScript، لذلك يكمن الفرق كله فيما تكتشفه قبل أن تنشر الكود.
في JavaScript تكون الدالة نفسها هي هذا الكود بعد حذف type Product = ... و: Product[] و: number. تضيف الأنواع معلومات للمترجم وللمحرر، لكنها لا تغيّر ما يفعله البرنامج.
مقارنة سريعة بين TypeScript و JavaScript
| JavaScript | TypeScript | |
|---|---|---|
| نظام الأنواع | ديناميكي: الأنواع تخص القيم ولا تُعرف إلا وقت التشغيل | ثابت: الأنواع تُعلن أو تُستنتج وتُفحص وقت الترجمة |
| متى تظهر أخطاء الأنواع | عند تشغيل السطر (undefined وNaN وTypeError) | في المحرر أثناء الكتابة، وعند الترجمة |
| أين تعمل | المتصفحات وNode.js وDeno وBun مباشرة | الأماكن نفسها، بعد حذف الأنواع |
| خطوة البناء | لا حاجة إليها | tsc أو أداة تجميع، أو بيئة تشغيل تحذف الأنواع بنفسها |
| الملفات | .js و.mjs و.cjs | .ts و.mts و.cts و.tsx، إضافة إلى ملفات تعريف الأنواع .d.ts |
| سرعة التشغيل | خط الأساس | مطابقة: الناتج JavaScript |
| دعم المحرر | إكمال تلقائي من الأنواع المستنتجة وتعريفات المكتبات، وقد يكون ناقصًا | إكمال تلقائي وإعادة تسمية و«البحث عن كل المراجع» اعتمادًا على الأنواع المعلنة |
| منحنى التعلم | أقل | JavaScript إضافة إلى نظام الأنواع |
| المعيار | ECMAScript من TC39 | مشروع مفتوح المصدر من Microsoft يتبع ECMAScript |
الكود نفسه باللغتين
إليك دالة مكتوبة بـ JavaScript. لا شيء فيها يحدد الشكل الذي يجب أن يكون عليه user:
function greeting(user) {
return `Hello, ${user.firstName} ${user.lastName}`;
}
greeting({ firstname: "Ada", lastName: "Lovelace" });
// "Hello, undefined Lovelace", no error anywhere
تحدد نسخة TypeScript الشكل مرة واحدة، ويُبلَّغ عن الخطأ الإملائي قبل تشغيل الكود:
interface User {
firstName: string;
lastName: string;
}
function greeting(user: User): string {
return `Hello, ${user.firstName} ${user.lastName}`;
}
greeting({ firstname: "Ada", lastName: "Lovelace" });
// error TS2561: Object literal may only specify known properties,
// but 'firstname' does not exist in type 'User'. Did you mean to write 'firstName'?
تعليقات الأنواع هي كل الفرق في الصياغة. تضيف TypeScript أيضًا بعض التصريحات الخاصة بها (interface وtype وenum، والأنواع العامة مثل Array<string>، ومحددات الوصول مثل private)، لكن الجمل والعوامل والكائنات المدمجة كلها من JavaScript.
ما تكتشفه TypeScript ولا تكتشفه JavaScript
تحوّل JavaScript الأنواع بصمت. هذا الخطأ شائع مع القيم القادمة من حقول النماذج، وهي نصوص دائمًا. شغّله لترى ما يقوله المترجم:
index.ts(7,17): error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'number[]'.
Type 'string' is not assignable to type 'number'.
في JavaScript يعمل هذا الكود ويطبع 010205، لأن 0 + "10" دمج نصوص. أما TypeScript فترفض الترجمة حتى تُحوَّل النصوص، مثلًا باستخدام fromForm.map(Number).
الفئة الكبيرة الأخرى هي القيم التي قد تكون غائبة. تعيد Array.prototype.find القيمة undefined عندما لا يطابق شيء، وتجبرك TypeScript على التعامل مع ذلك:
index.ts(8,13): error TS18048: 'user' is possibly 'undefined'.
نسخة JavaScript العادية تنهار وقت التشغيل بالخطأ TypeError: Cannot read properties of undefined (reading 'name'). والحل في TypeScript هو معالجة الحالة التي أشار إليها المترجم:
الناتج:
GRACE
no user with id 3
ما لا تكتشفه TypeScript: الأخطاء المنطقية (المعادلة الخاطئة نوعها صحيح)، وأي شيء يتعلق ببيانات تدخل البرنامج وقت التشغيل. استجابة API نوعها User لا تكون صحيحة إلا بقدر صحة الخادم الذي أرسلها، لأن الأنواع تختفي حين يعمل الكود. افحص مثل هذه البيانات بكود يعمل وقت التشغيل.
استخدام مكتبات JavaScript في TypeScript
كل حزمة npm تعمل من TypeScript، لأن الناتج JavaScript في كل الأحوال. تأتي أنواع الحزمة من أحد ثلاثة مصادر:
- الحزمة تحتوي على ملفات
.d.tsخاصة بها. معظم الحزم التي تُصان بنشاط تفعل ذلك، ولا تحتاج إلى تثبيت شيء إضافي. - حزمة
@typesمنفصلة من مشروع DefinitelyTyped المجتمعي: الأمرnpm install --save-dev @types/lodashيضيف أنواعlodash. - لا مصدر. عندها، مع تفعيل
strict، يصبح الاستيراد نفسه خطأ:
error TS7016: Could not find a declaration file for module 'lodash'. '/project/node_modules/lodash/lodash.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/lodash` if it exists or add a new declaration (.d.ts) file containing `declare module 'lodash';`
الحل هو تثبيت حزمة @types إن وُجدت، أو وصف الوحدة بنفسك في ملف .d.ts؛ وتشرح صفحة ملفات التعريف كيف تفعل ذلك.
خطوة البناء
المتصفحات وNode.js لا تفحص الأنواع، لذلك تحتاج TypeScript إلى خطوة بين الكود المصدري والكود الذي يعمل. هناك ثلاثة إعدادات شائعة:
tscيترجم كل شيء. يفحص الأنواع ويكتب ملفات.js، عادة داخل مجلدdist. إعداد بسيط، وهو المعيار للمكتبات.- أداة تجميع أو خادم تطوير يحذف الأنواع، و
tsc --noEmitيفحصها. تحذف Vite وesbuild الأنواع دون فحصها، ما يبقي إعادة التحميل سريعة؛ ويشغّل المحرر وخطوة في CI فاحص الأنواع. - بيئة التشغيل تحذف الأنواع. إصدارات Node.js الحالية وDeno وBun تشغّل ملفات
.tsمباشرة. لا تفحص أي منها الأنواع أثناء التشغيل، لذلك يبقىtsc --noEmit(أوdeno check) هو طريقة العثور على أخطاء الأنواع.
لا تحتاج JavaScript إلى أي من ذلك، وهذه أكبر ميزة عملية لها في السكربتات الصغيرة. تكلفة خطوة TypeScript هي في الغالب الإعداد، أي ملف tsconfig.json واعتمادية تطوير typescript، إضافة إلى وقت الترجمة؛ وقد خفّض المترجم الأصلي في TypeScript 7 هذا الوقت بنحو عشر مرات في المشاريع الكبيرة.
منحنى التعلم
كل ما تعرفه عن JavaScript ينتقل معك، لأن وقت تشغيل TypeScript هو JavaScript. الجديد هو نظام الأنواع، ويأتي على طبقات:
- تعليقات الأنواع على المتغيرات والمعاملات والقيم المُعادة (
: stringو: number[]). - أنواع الكائنات باستخدام
interfaceوtype، والخصائص الاختيارية، وunion مثلstring | number. - التضييق: فحص القيمة بـ
typeofأوinأو===حتى يعرف المترجم في أي حالة أنت. - الأنواع العامة، وأنواع الأدوات مثل
Partial<T>وPick<T, K>، والأنواع المتقدمة لمؤلفي المكتبات.
تغطي الطبقتان الأوليان معظم كود التطبيقات. كثير من الأنواع يُستنتج تلقائيًا، لذلك يبدو جزء كبير من كود TypeScript مثل JavaScript مع أنواع على توقيعات الدوال فقط.
متى تختار TypeScript أو JavaScript
هل TypeScript أفضل من JavaScript؟ في الكود الذي يصونه عدة أشخاص أو يعيش سنوات، نعم في الغالب، وقد اتجهت الصناعة في هذا الاتجاه: بحسب عدد المساهمين الشهريين الذي يحسبه GitHub، تجاوزت TypeScript كلًا من JavaScript وPython في أغسطس 2025 لتصبح أكثر لغة مستخدمة على GitHub. أما في السكربتات الصغيرة، فغالبًا ما تكون JavaScript العادية الأداة الأنسب.
اختر TypeScript عندما:
- يعمل على الكود أكثر من شخص، أو سيُصان لأشهر أو سنوات.
- تكون قاعدة الكود كبيرة إلى حد لا تستطيع معه تذكر توقيع كل دالة.
- تعيد الهيكلة كثيرًا: إعادة تسمية خاصية تحدّث كل استخداماتها، ويسرد المترجم ما تبقى.
- تنشر مكتبة: تمنح ملفات
.d.tsمستخدميها إكمالًا تلقائيًا وفحوصًا. - يتوقعها إطار العمل. تطبيقات Angular تُكتب بـ TypeScript، وNext.js وAstro ينشئان المشاريع الجديدة بـ TypeScript افتراضيًا، وقوالب Vite لـ React وVue وSvelte يأتي كل منها بنسخة TypeScript.
اختر JavaScript عندما:
- يكون البرنامج سكربتًا قصيرًا، أو تجربة لمرة واحدة، أو مقتطف كود في وحدة تحكم المتصفح.
- تتعلم البرمجة لأول مرة وتريد مفاهيم أقل في الوقت نفسه.
- لا توجد خطوة بناء ولا تريد واحدة. وحتى عندها، يمنحك
// @ts-checkمع JSDoc بعض الفحص في ملف.jsعادي.
ترحيل مشروع JavaScript إلى TypeScript
لا يلزم أن يحدث الترحيل دفعة واحدة. يقبل المترجم JavaScript بجانب TypeScript:
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
مع allowJs تُترجم ملفات .js ويمكنها الاستيراد من ملفات .ts والعكس. بعد ذلك حوّل تدريجيًا:
- أعد تسمية ملف واحد من
.jsإلى.tsوأصلح الأخطاء التي يبلّغ عنها المترجم فيه. - ابدأ بالأطراف (وحدات الأدوات المساعدة ذات الاستيرادات القليلة)، ثم تقدّم نحو الداخل.
- فعّل
checkJs، أو أضف// @ts-checkفي أعلى ملفات.jsبعينها، لفحص أنواع الملفات التي لم تُعد تسميتها بعد.
في ملفات JavaScript المفحوصة، توفر تعليقات JSDoc الأنواع:
// @ts-check
/**
* @param {number} price
* @param {number} qty
* @returns {number}
*/
function lineTotal(price, qty) {
return price * qty;
}
lineTotal("3", 2);
// error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
تتوقف بعض الفرق عند هذه النقطة: ملفات JavaScript مع أنواع JSDoc يفحصها tsc، دون خطوة بناء للكود نفسه. وتمضي فرق أخرى حتى النهاية إلى .ts. إذا فعّلت strict في مشروع قائم، فتوقع أخطاء كثيرة في البداية؛ وتسرد صفحة الوضع الصارم ما يفحصه كل خيار حتى تفعّل الخيارات واحدًا تلو الآخر.
الأسئلة الشائعة
ما الفرق الأساسي بين TypeScript و JavaScript؟
تضيف TypeScript أنواعًا ثابتة إلى JavaScript. تصف ما هي كل قيمة (name: string وitems: Item[])، فيبلّغ مترجم TypeScript عن الأخطاء قبل تشغيل الكود. أما JavaScript فلا تفحص شيئًا مسبقًا: النوع الخاطئ لا يظهر إلا عند تنفيذ ذلك السطر، وغالبًا على شكل undefined أو TypeError.
هل TypeScript أفضل من JavaScript؟
في معظم المشاريع التي يصونها أكثر من شخص، أو التي تعيش أكثر من بضعة أسابيع، نعم: تكتشف الأنواع فئات كاملة من الأخطاء، وتجعل إعادة الهيكلة آمنة، وتشغّل الإكمال التلقائي في المحرر. أما في سكربت قصير أو نموذج أولي سريع أو تمرين تعليمي، فالبدء بـ JavaScript العادية أسرع ولا يحتاج إلى إعداد بناء.
هل TypeScript أسرع من JavaScript؟
لا، وليست أبطأ أيضًا. تُترجم TypeScript إلى JavaScript وتُمحى الأنواع، فالكود الذي يعمل هو JavaScript نفسها التي كنت ستكتبها يدويًا. التكلفة الإضافية الوحيدة هي وقت الترجمة أثناء التطوير.
هل أتعلم JavaScript أم TypeScript أولًا؟
تعلّم أساسيات JavaScript أولًا أو مع TypeScript. كل السلوك وقت التشغيل (المتغيرات والدوال والكائنات والمصفوفات والوعود) هو JavaScript، وTypeScript تصفه فقط. عندما تستطيع كتابة برامج JavaScript صغيرة، تصبح إضافة الأنواع خطوة قصيرة.
هل يمكن استخدام TypeScript و JavaScript في المشروع نفسه؟
نعم. اضبط "allowJs": true في tsconfig.json فيقبل المترجم ملفات .js بجانب ملفات .ts. أضف "checkJs": true (أو تعليق // @ts-check في كل ملف) لفحص أنواع ملفات JavaScript أيضًا، اعتمادًا على الأنواع المكتوبة في تعليقات JSDoc. هذه هي الطريقة المعتادة لترحيل مشروع ملفًا بعد ملف.