كيفية دمج TypeScript في مشروع JavaScript قائم: دليل شامل
مقدمة: تحديات JavaScript وحلول TypeScript
لطالما كانت كتابة الأكواد تجربة ممتعة، ولكن مع مشاريع JavaScript الكبيرة والمعقدة، تظهر تحديات متكررة قد تؤثر على جودة المنتج النهائي. فكثيراً ما نصادف أخطاء مثل cannot read property <> of undefined، أو السلسلة النصية الغامضة [Object object]، وحتى استدعاءات الدوال بعدد غير صحيح من المعاملات. هذه المشكلات تتفاقم في قواعد الأكواد الضخمة، مما يجعل عملية التطوير والصيانة أكثر صعوبة.
لحسن الحظ، هناك حلول متقدمة يمكن أن تحسن هذه التجربة بشكل كبير. في هذا المقال، لن نركز على استعراض مزايا TypeScript الرائعة (فهي كذلك بالفعل)، بل سنتعمق في الخطوات العملية التي تحتاج لإكمالها لترحيل مشروع JavaScript العادي إلى مشروع هجين يدعم TypeScript. بنهاية هذا الدليل، ستكون قادراً على الإجابة على الأسئلة التالية:
- كيف يمكنني إضافة الأنواع إلى مشروع
JavaScriptالخاص بي؟ - ما هو
TypeScript؟ - كيف يمكنني استخدام
TypeScriptفي مشروعJavaScript؟ - ما هي خطوات تحويل تطبيق
JavaScriptلدعمTypeScript؟ - كيف أتعامل مع عملية البناء والتعبئة (Build & Packaging)؟
- كيف أضبط أدوات الفحص (Linting)؟
- كيف يمكنني إقناع مؤسستي والمطورين بتبني
TypeScript؟

إضافة الأنواع إلى مشروع JavaScript الخاص بك: الضرورة والحلول
لا يدعم JavaScript الأصيل (Vanilla JavaScript) الأنواع بشكل مباشر في الوقت الحالي، لذا نحتاج إلى طبقة تجريدية فوقه لتحقيق ذلك. من بين الحلول الشائعة، نجد مدقق الأنواع الساكن من Facebook المسمى flow، ولغة Microsoft المسماة typescript. سيركز هذا المقال على استكشاف استخدام وإضافة TypeScript إلى مشروع JavaScript الخاص بك.
ما هو TypeScript؟ فهم الأساسيات
TypeScript هو مجموعة عليا (Superset) من JavaScript، مدعومة بالأنواع، ويتم تحويلها برمجياً (Compiles) إلى JavaScript عادي. هذا يعني أن أي كود JavaScript صالح هو أيضاً كود TypeScript صالح.

إذا كنت تعرف JavaScript، فقد قطعت بالفعل أكثر من نصف الطريق. يتكون TypeScript من عدة أجزاء رئيسية:
- لغة
TypeScript: هي لغة جديدة تحتوي على جميع ميزاتJavaScript، بالإضافة إلى ميزات إضافية للأنواع. يمكنك مراجعة المواصفات لمزيد من المعلومات. - مترجم
TypeScript(tsc): هو محرك نظام الأنواع الذي يقوم ببناء ملفات.tsوينتج عنها ملفات.js.
مثال: ‘Hello World’ في TypeScript
لإنشاء أول تطبيق TypeScript، اتبع الخطوات التالية:
- ثبت
TypeScriptباستخدام الأمرnpm i typescript. - أنشئ مجلداً باسم
exampleوانتقل إليه عبر الطرفية (cd example). - أنشئ ملفاً باسم
hello.world.ts. - اكتب الكود التالي فيه ثم احفظه:
const firstWords: string = "hello world"; console.info(firstWords); - شغل الأمر
tscلتشغيل مترجمTypeScriptعلى المجلد الحالي. - ستلاحظ ظهور ملف
hello.jsالذي يمكنك تشغيله الآن. - شغل الأمر
node ./hello.js.
استراتيجيات دمج TypeScript في مشاريع JavaScript القائمة
هناك عدة استراتيجيات لعملية “الترحيل” هذه (على مستوى الشركة والكود). لقد قمت بإدراجها أدناه حسب “التكلفة” والقيمة التي توفرها. أقترح البدء بـ “دعم TS للتطبيق” والانتقال إلى الأمام بعد أن تثبت قيمته لفريق التطوير الخاص بك.

عملية ترحيل TypeScript: كرر العملية فقط إذا أثبتت قيمتها.
النهج التدريجي: إضافة دعم TypeScript للتطبيقات الحالية

اقتراحي الأول هو إنشاء مزيج من اللغتين في مشروع واحد، ثم كتابة جميع الأكواد “المستقبلية” بلغة TypeScript. قد يبدو دمج لغتين في مشروع واحد أمراً سيئاً في البداية، لكنه يعمل بشكل جيد لأن TS صُمم للاستخدام التدريجي. في البداية، يمكن استخدامه تماماً مثل JS مع ملفات .ts وخطوط استيراد غريبة. في هذه الاستراتيجية، سنقوم بتحويل ملفات TypeScript المرحلة ونسخ ملفات JavaScript فقط إلى مجلد الإخراج.
الفائدة الكبيرة لهذا النهج هي أنه يسمح بمنحنى تعليمي تدريجي لفريق التطوير (ولك) مع اللغة وميزاتها. كما يمنحك خبرة عملية ورؤية ثاقبة حول إيجابياتها وسلبياتها. أوصي بشدة بالبدء من هذه الخطوة ثم تكرارها مع فريقك قبل المضي قدماً. للحصول على دليل سريع حول “كيفية القيام بذلك”، انتقل إلى قسم الخطوات العملية لتحويل تطبيق JavaScript لدعم TypeScript.
نهج المكتبات: إضافة دعم TypeScript للمكتبات الداخلية
بعد أن تكتسب بعض الخبرة العملية مع TS ويوافق فريق التطوير الخاص بك على أن الأمر يستحق المضي قدماً، أقترح تحويل مكتباتك ووحداتك الداخلية لدعم TS. يمكن القيام بذلك بطريقتين:
- الطريقة الأولى: ملفات التعريف (Declaration Files): تتضمن إضافة بسيطة لملفات
.d.ts، مما يساعد مترجمTSعلى التحقق من أنواع كودJavaScriptالموجود ويوفر لك دعم الإكمال التلقائي في بيئة التطوير المتكاملة (IDE) الخاصة بك. هذا هو الخيار “الأقل تكلفة”، لأنه لا يتطلب أي تغييرات في الكود للمكتبة على الإطلاق. كما يمنحك أقصى قدر من القوة ودعم الأنواع في الكود المستقبلي الخاص بك. - الطريقة الثانية: إعادة كتابة كاملة (Full Re-write): تتمثل في إعادة كتابة شاملة بلغة
TypeScript، والتي قد تستغرق وقتاً طويلاً وتكون عرضة للأخطاء. أنصح بعدم القيام بذلك، إلا إذا أثبتت عائد استثمار (ROI) مجدياً لفريقك.
النموذج الأولي (Skeleton): خطوة نحو المستقبل

أفترض أن معظم المطورين “كسالى” وعادة ما يبدأون تطبيقاتهم بالنسخ من نموذج أولي (والذي عادة ما يحتوي على التسجيل، المقاييس، التكوين، وما إلى ذلك). تساعدك هذه الخطوة على شق طريقك نحو مستقبل مشرق، من خلال إنشاء نموذج أولي “رسمي” لشركتك. سيكون هذا النموذج بنسبة 100% TS، ويلغي النموذج الأولي القديم لـ JS إذا كان موجوداً. مشروع typescript-node-starter هو مشروع أول جيد جداً للبدء به.
النهج الشامل: تحويل قاعدة كود كاملة من JavaScript إلى TypeScript

يتطلب هذا الخيار إعادة كتابة كاملة من كود JavaScript إلى TypeScript. أوصي بالقيام بذلك كخطوة أخيرة في عملية ترحيل TS لأنه يتطلب إعادة كتابة كاملة للتطبيق ومعرفة عميقة بـ TypeScript وميزاته. يمكنك القيام بهذه الإعادة الكتابية (وهي عملية طويلة) بالطريقة التالية:
- حدد أنواعاً واضحة لمنطق عمل تطبيقك، واجهات برمجة التطبيقات (API)، وطلبات
HTTP. - استخدم حزم
@typesلجميع المكتبات في ملفpackage.jsonالخاص بك. تدعم معظم المكتبات الموجودةTS، وفي هذه العملية أقترح ترحيلها واحدة تلو الأخرى (بمجرد إضافة@types/<package_name>في ملفpackage.jsonالخاص بك). - حوّل المكونات المنطقية لتطبيقك بترتيب أهميتها. كلما كان منطق العمل فريداً، كان ذلك أفضل.
- حوّل أجزاء الإدخال/الإخراج (IO) لتطبيقك، طبقات قواعد البيانات، قوائم الانتظار، وما إلى ذلك.
- حوّل اختباراتك.

ضع في اعتبارك أن هناك أدوات آلية مصممة لتسهيل هذه العملية، على سبيل المثال ts-migrate من فريق Airbnb. تعالج هذه الأداة المشكلة من منظور مختلف، وتحول جميع الملفات إلى TypeScript. كما أنها تسمح بتحسينات تدريجية (كما ذكر في الخطوات أعلاه) بينما تكون قاعدة الكود بأكملها TypeScript من اليوم الأول.
الخطوات العملية لتحويل تطبيق JavaScript لدعم TypeScript
تثبيت TypeScript
ثبت TypeScript عن طريق تشغيل الأمر: npm install typescript.
ملف إعداد TypeScript
أضف ملف إعداد TypeScript، والذي يمكن إنشاؤه باستخدام الأمر tsc --init في واجهة سطر الأوامر (CLI) الخاصة بك. إليك مثال على شكل إعدادنا الأولي:
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "allowJs": true, "checkJs": false, "outDir": "dist", "rootDir": ".", "strict": false, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ "declaration": true, /* Generates corresponding '.d.ts' file. */ "strictNullChecks": true, "resolveJsonModule": true, "sourceMap": true, "baseUrl": ".", "paths": { "*": [ "*", "src/*", "src/setup/*", "src/logic/*", "src/models/*", "config/*" ] } }, "exclude": [ "node_modules", "dist" ], "include": [ "./src", "./test", "./*", "./config" ]}
بعض الأمور التي يجب ملاحظتها أعلاه:
- نقوم بقراءة جميع الملفات في مجلدات
srcأوtestأوconfig(باستخدام علامةinclude). - نقبل ملفات
JavaScriptكمدخلات (باستخدام علامةallowJs). - نصدر جميع ملفات الإخراج في مجلد
build(باستخدام علامةoutDir).
إنشاء أول ملف TS في مشروعك
أوصي بالبدء بإضافة ملف TypeScript بسيط (أو تغيير ملف JS بسيط جداً إلى ملف TS) ونشره. خذ هذا الترحيل خطوة بخطوة.
تعديل ملف package.json
إليك كيف يبدو ملف package.json الخاص بنا قبل وبعد التعديل:
قبل التعديل:
{ "scripts": { "start": "node ./application.js", "mocha": "mocha --recursive --reporter spec -r test/bootstrap.js", "test": "npm run mocha -- test/ -r test/integration/bootstrap.js" }}
بعد التعديل:
{ "scripts": { "start": "node ./dist/application.js", "build-dist": "./node_modules/typescript/bin/tsc", "mocha": "mocha --recursive --reporter spec -r ./dist/test/bootstrap.js", "test": "npm run mocha -- ./dist/test/ -r ./dist/test/integration/bootstrap.js" }}
كما ترى، معظم التغييرات كانت تتعلق بإضافة البادئة dist لمعظم أوامر البناء لدينا. أضفنا أيضاً سكربت build-dist الذي يقوم بتحويل قاعدة الأكواد لدينا ونقل جميع الملفات إلى مجلد مخصص يسمى dist.
إضافة دعم خرائط المصدر (Source-Map Support)
إحدى المشكلات الكبيرة عند إضافة TypeScript إلى مشروعك هي أنك تضيف طبقة من عدم المباشرة بين الكود الذي تكتبه والكود الذي يعمل فعلياً في الإنتاج (حيث يتم تحويل .ts إلى .js في وقت التشغيل). على سبيل المثال، تخيل برنامج TypeScript التالي:
const errorMessage: string = "this is bad" throw new Error(a)
عند تشغيله، سيطلق تتبع مكدس (stack-trace) كالتالي:
Error: this is bad at Object.<anonymous> (/Users/dorsev/work/git/example/hello.js:3:7)
هذا يمثل مشكلة لأن قاعدة الأكواد لدينا تحتوي فقط على ملفات .ts. وبما أن معظم أكواد الإنتاج تحتوي على مئات الأسطر، فسيكون ترجمة هذه الأرقام والملفات بشكل صحيح أمراً مستهلكاً للوقت حقاً. لحسن الحظ، هناك حل لهذه المشكلة يسمى source-map-support! يتيح لنا هذا التأكد من أن تتبعات المكدس ستحتوي على أسماء ملفات .ts الصحيحة وأرقام الأسطر كما اعتدنا عليها.
يمكن القيام بذلك عن طريق تشغيل npm install source-map-support ثم إضافة السطر التالي في الأسطر الأولى من تطبيقك: require('source-map-support').install();
يبدو الكود الآن كالتالي:
require('source-map-support').install(); const a: string = "this is bad"; throw new Error(a);
وعندما نقوم بتحويله، نشغل tsc --sourcemap hello.ts. نحصل الآن على تتبع المكدس التالي وهو رائع:
Error: this is bad at Object.<anonymous> (/Users/dorsev/work/git/example/hello.ts:3:7)
في الإصدارات الحديثة من nodejs، يتم دعم هذا بشكل أصلي باستخدام علامة --enable-source-maps.
إدارة عملية البناء والتعبئة (Build & Packaging) مع TypeScript
دعنا نلقي نظرة على التغييرات قبل وبعد في ملف إعداد البناء الخاص بنا (Travis).
ملف .travis قبل التعديل (نسخة مبسطة):
jobs: include: - &build-and-publish before_script: - npm install --no-optional --production - npm prune --production before_deploy: - XZ_OPT=-0 tar --exclude=.git --exclude=reports.xml --exclude=${ARTIFACTS_MAIN_DIR} --exclude=.travis.yml --exclude=test -cJf "${ARTIFACTS_PATH}/${REPO_NAME}" .tar.xz * .??* - &test before_script: - npm install --no-optional script: - echo "Running tests" - npm run lint && npm test
ملف .travis بعد التعديل:
jobs: include: - &build-and-publish before_script: - npm install --no-optional --production - npm run build-dist # Build dist folder - npm prune --production before_deploy: - cp -rf config/env-templates ./dist/config/ - cp -rf node_modules ./dist/ - cd dist - XZ_OPT=-0 tar --exclude=.git --exclude=reports.xml --exclude=${ARTIFACTS_MAIN_DIR} --exclude=.travis.yml --exclude=test -cJf "${REPO_NAME}.tar.xz" * - mv ${REPO_NAME}.tar.xz "../${ARTIFACTS_PATH}" - cd .. - &test before_script: - npm install --no-optional - npm run build-dist script: - echo "Running tests" - npm run lint && npm test
لاحظ أن معظم التغييرات تتعلق بـ “التعبئة” في ملف tar.xz وتشغيل الأمر build-dist قبل الوصول إلى مجلد dist.
ضبط أدوات الفحص (Linting) لمشاريع TypeScript
تتوفر العديد من حلول الفحص (linting). كان الحل الأول الذي استخدمناه هو tsfmt – لكننا قررنا لاحقاً عدم استخدامه لأنه يتطلب منك الحفاظ على تكوينين منفصلين لمشروعك (أحدهما لـ TypeScript باستخدام tsfmt والآخر لـ JavaScript باستخدام eslint). يبدو المشروع أيضاً مهجوراً. ثم وجدنا TSLint الذي وجهنا إلى مكون eslint الإضافي لـ TypeScript. ثم قمنا بتكوينه على النحو التالي:
ملف eslintrc.js الخاص بنا:
module.exports = { rules: { indent: [2, 2, { SwitchCase: 1 }], 'no-multi-spaces': 2, 'no-trailing-spaces': 2, 'space-before-blocks': 2, }, overrides: [{ files: ['**/*.ts'], parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint'], extends: ['plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended'] }]}
قمنا بتكوينه للتشغيل باستخدام أمر lint-fix في ملف package.json الخاص بنا والذي يبدو كالتالي:
{ "scripts": { "lint-fix": "node_modules/.bin/eslint . --fix" }, "pre-commit": [ "lint-fix" ]}
كيفية إقناع فريقك ومؤسستك بتبني TypeScript
أعتقد أن أحد أهم الجوانب عند تقديم TypeScript إلى مؤسستك هو “العرض التقديمي” وكيفية تقديمه لفريق التطوير الخاص بك. إليك العرض التقديمي الذي قدمناه داخلياً والذي دار حول المواضيع التالية:
- اشرح لماذا نعتقد أن
TypeScriptرائع: ركز على الفوائد الملموسة التي سيجنيها الفريق. - ما هو
TypeScript: قدم تعريفاً واضحاً وموجزاً. - أمثلة كود أساسية: النقطة الرئيسية في هذا الجزء ليست “تعليم”
TypeScriptبنسبة 100%، لأن الناس سيفعلون ذلك بمفردهم. بدلاً من ذلك، امنح الناس شعوراً بأنهم يستطيعون قراءة وكتابةTypeScript، وأن منحنى التعلم ليس صعباً للغاية. - أمثلة كود متقدمة: مثل أنواع الاتحاد (Union types) وأنواع البيانات الجبرية (Algebraic data-types) التي توفر قيماً هائلة لمطور
JS. هذه هي مكافآت حقيقية، بالإضافة إلى اللغة ذات الأنواع والمترجم الذي سيجذب مطوريك إليها. - كيفية البدء في استخدامه: شجع الناس على تنزيل بيئة التطوير المتكاملة
vs-code IDEوإضافة تعليق توضيحي (//@ts-check) حتى يتمكنوا من البدء في رؤية السحر! في شركتنا، أعددنا مسبقاً بعض الأخطاء الرائعة التي يكتشفهاts-check، وقمنا بعرض حي (2-3 دقائق) لإظهار مدى سرعة مترجمTypeScriptفي مساعدتهم باستخدام وثائقJSمع تعليقات الأنواع التوضيحية أوts-check. - الغوص العميق في بعض الميزات: اشرح ملفات
ts.dوحزم@typesوهي بعض الأشياء التي ستواجهها مبكراً جداً في قواعد أكوادTypeScriptالخاصة بك. - طلبات سحب (PRs) حية من عملك: عرضنا طلب السحب الذي أنشأناه مبكراً، وشجعنا الناس على مراجعته وتجربته بأنفسهم.
- شارك بعض الموارد الرائعة: هناك الكثير من المحتوى عبر الإنترنت، ومن الصعب تمييز الجيد من السيئ. قدم خدمة لزملائك في الفريق وابحث بعمق وحاول العثور على محتوى عالي الجودة حول الأدوات التي تستخدمها وتحتاجها. انتقل إلى الخلاصة للحصول على مواردي.
- أنشئ طلب سحب عام: أوصي بمحاولة الحصول على أكبر قدر ممكن من الدعم للموافقة عليه.

اخلق حماساً إيجابياً في مؤسستك حول التغيير!

أوصي بشدة بتعديل هذه القائمة وفقاً لفريقك ومعاييرك وقيود الوقت.
الخلاصة التقنية
TypeScript أداة قوية للغاية لا غنى عنها في تطوير البرمجيات الاحترافية. إذا كنت تعمل على تطوير برمجيات ذات جودة إنتاجية عالية وتتطلب متطلبات العمل وتوافرها مستوى عالياً، فإنني أشجعك بشدة على تجربة TypeScript. تذكر دائماً أن تأخذ الأمر خطوة بخطوة. اللغات والأطر الجديدة صعبة، لذا خصص الوقت للتعلم وتثقيف نفسك وفريقك قبل دفع هذه العملية إلى الأمام.
أنشئ حلقة تغذية راجعة قصيرة وقيمة مقترحة واضحة. من الصعب “بيع” لغة جديدة لفريقك والإدارة لأنها تستغرق وقتاً وموارد. لذا، صمم عملية الترحيل الخاصة بك بحلقات تغذية راجعة قصيرة، وحاول تحديد مؤشرات أداء رئيسية (KPIs) واضحة (عدد أقل من الأخطاء في الإنتاج، أوقات إعادة هيكلة أسهل، وما إلى ذلك) وتأكد من أن القيمة المقترحة لحالة الاستخدام الخاصة بك مبررة باستمرار حتى تصبح المعيار الفعلي.
اجعل موارد التعلم متاحة بسهولة. لقد استمتعت حقاً بهذه المحاضرة حول الخطوات الأولى في TypeScript و منشور المدونة هذا حول الترحيل التدريجي إلى TypeScript. أيضاً، لا تفوت مشروع deno ومشروع ts-node. أنا متحمس جداً وأتطلع إلى استخدامهما قريباً.