{"id":"l-at-5-4","courseId":"advanced-typescript","lang":"ar","kind":"lesson","summary":"رحّل قاعدة كود JavaScript إلى TypeScript ملفًا بعد ملف بينما تستمر في نشر التحديثات، وحافظ على سرعة فحص الأنواع مع نموّ قاعدة الكود وأنواعها.","html":"<p>الترحيل الذي قُدته نقل نظام دفع وإدارة طلبات مكتوبًا بـ 400,000 سطر من JavaScript إلى TypeScript صارمة خلال نحو ثمانية عشر شهرًا، دون تجميد للميزات ودون branch طويل العمر. ونشرنا التحديثات كل يوم طوال تلك المدة. تقنيات مستوى الأنواع في هذه الدورة مهمة، لكن في قاعدة كود حقيقية <em>العملية</em> هي ما يقرّر إن كان الترحيل سيكتمل. هذا الدرس هو تلك العملية، إضافة إلى كيفية الحفاظ على سرعة فحص الأنواع بعد أن تنتهي.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">التعايش أولًا</h2>\n<p>ابدأ بالسماح لـ TypeScript وJavaScript بالعيش معًا:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"json\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">tsconfig.json</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  \"compilerOptions\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: {</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"allowJs\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">true</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"checkJs\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">false</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"noEmit\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">true</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"strict\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">true</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"module\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"esnext\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"moduleResolution\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"bundler\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"target\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"es2022\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"skipLibCheck\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">true</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  },</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  \"include\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"src\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p><code dir=\"ltr\">allowJs</code> تُدخل ملفات <code dir=\"ltr\">.js</code> إلى البرنامج، فتستطيع ملفات <code dir=\"ltr\">.ts</code> استيرادها والعكس. و<code dir=\"ltr\">noEmit</code> تترك البناء للـ bundler الموجود؛ و<code dir=\"ltr\">tsc --noEmit</code> يعمل في CI كفاحص فقط. و<code dir=\"ltr\">strict</code> مفعَّل منذ اليوم الأول، لكنه لا ينطبق إلا على ملفات <code dir=\"ltr\">.ts</code>، فيُحاسَب الكود الجديد بالمعيار الكامل ويُترك القديم وشأنه. ادمج هذا على <code dir=\"ltr\">main</code> في pull request صغير واحد. لا شيء في التطبيق يتغيّر.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">افحص JavaScript قبل تحويلها</h2>\n<p>يمكنك الحصول على فحص حقيقي في ملف <code dir=\"ltr\">.js</code> دون إعادة تسميته. أضف <code dir=\"ltr\">// @ts-check</code> في أعلاه، فيفحص TypeScript الملف، قارئًا الأنواع من تعليقات JSDoc:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">src/money.js</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// @ts-check</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">/** </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@typedef</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> {'USD' | 'EUR' | 'EGP'}</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Currency</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"> */</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">/** </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@typedef</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> {{ cents: number; currency: Currency }}</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Money</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\"> */</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">/**</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"> * </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@param</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> {Money}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> a</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"> * </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@param</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> {Money}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"> * </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@returns</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> {Money}</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"> */</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> add</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (a.currency </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!==</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b.currency) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">throw</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Error</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Currency mismatch'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { cents: a.cents </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b.cents, currency: a.currency };</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>إنه الفاحص نفسه، بالقواعد نفسها، على ملف JavaScript عادي. استخدمه على أكثر الملفات حساسية وخطورة أولًا. ويستطيع JSDoc حتى استيراد أنواع من ملفات <code dir=\"ltr\">.ts</code> بـ <code dir=\"ltr\">@import</code> أو <code dir=\"ltr\">import('./types').Order</code>، فيستخدم ملف JavaScript أنواع المجال التي كتبتها أصلًا.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">حوّل من الأوراق نحو الداخل</h2>\n<p>حين تعيد تسمية الملفات فعلًا، يكون الترتيب مهمًا. ارسم (أو ولّد) مخطّط الـ imports وابدأ من <strong>الأوراق</strong>: الوحدات التي لا تستورد شيئًا آخر من قاعدة كودك، مثل دوال المال المساعدة، والتنسيق، والثوابت، وأنواع المجال. ثم حوّل الوحدات التي لا تعتمد إلا على وحدات محوَّلة، واستمر في العمل نحو الداخل باتجاه نقاط الدخول.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 250\" role=\"img\" aria-labelledby=\"l-at-5-4-t1\"><defs><marker id=\"arrow-l-at-5-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-at-5-4-t1\">مخطّط الـ imports في نظام الدفع. صفحة الدفع تستورد عميل الـ API ومخزن السلة، وكلاهما يستورد أداتي المال والتنسيق. الأوراق، أي المال والتنسيق، صارت TypeScript؛ وعميل الـ API ومخزن السلة قيد العمل؛ وصفحة الدفع ما زالت JavaScript. يسير التحويل من الأسفل إلى الأعلى.</title>\n  <rect class=\"d-box\" x=\"260\" y=\"16\" width=\"180\" height=\"46\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"350\" y=\"44\" text-anchor=\"middle\">checkoutPage.js</text>\n  <rect class=\"d-box-warn\" x=\"110\" y=\"104\" width=\"180\" height=\"46\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"200\" y=\"132\" text-anchor=\"middle\">apiClient.ts</text>\n  <rect class=\"d-box-warn\" x=\"410\" y=\"104\" width=\"180\" height=\"46\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"500\" y=\"132\" text-anchor=\"middle\">cartStore.js</text>\n  <rect class=\"d-box-success\" x=\"110\" y=\"192\" width=\"180\" height=\"46\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"200\" y=\"220\" text-anchor=\"middle\">money.ts</text>\n  <rect class=\"d-box-success\" x=\"410\" y=\"192\" width=\"180\" height=\"46\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"500\" y=\"220\" text-anchor=\"middle\">format.ts</text>\n  <path class=\"d-arrow\" d=\"M310 62 L230 102\" marker-end=\"url(#arrow-l-at-5-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M390 62 L470 102\" marker-end=\"url(#arrow-l-at-5-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M200 150 L200 188\" marker-end=\"url(#arrow-l-at-5-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M500 150 L500 188\" marker-end=\"url(#arrow-l-at-5-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M270 150 L440 190\" marker-end=\"url(#arrow-l-at-5-4-1)\"/>\n  <text class=\"d-label-muted\" x=\"640\" y=\"132\" text-anchor=\"middle\">قيد العمل</text>\n  <text class=\"d-label-muted\" x=\"640\" y=\"220\" text-anchor=\"middle\">أولًا</text>\n  <text class=\"d-label-muted\" x=\"640\" y=\"44\" text-anchor=\"middle\">أخيرًا</text>\n</svg><figcaption>حوّل الأوراق أولًا، كي يستورد كل ملف TypeScript جديد كودًا محدَّد الأنواع</figcaption></figure>\n<p>تحويل نقطة دخول أولًا يعطيك ملف <code dir=\"ltr\">.ts</code> كل ما يستورده بلا أنواع، أي كثيرًا من <code dir=\"ltr\">any</code> وقليلًا جدًا من الفحص. أما تحويل ورقة أولًا فيعطي كل ملف لاحق أنواعًا حقيقية يبني عليها.</p>\n<p>وإلى جانب الأوراق، أعطِ الأولوية لـ <strong>الحدود</strong>: عميل الـ API، والمحلّلات، ونماذج المجال. <code dir=\"ltr\">Order</code> محدَّد النوع واستجابة API محلَّلة يحميان كل ملف يلمسهما، محوَّلًا كان أم لا. ولهذا تهمّ الأقسام 1 و2 و4 من هذه الدورة في الترحيل أكثر من القسم 3.</p>\n<p>حين يختفي آخر ملف <code dir=\"ltr\">.js</code>، أزِل <code dir=\"ltr\">allowJs</code>. ثم شدّ الخيارات الإضافية من <a href=\"/ar/lesson/advanced-typescript/l-at-5-1\">صرامة tsconfig التي تؤتي ثمارها</a> تدريجيًا، بدءًا بـ <code dir=\"ltr\">noUncheckedIndexedAccess</code>، واحدًا تلو الآخر، مُخفيًا الأخطاء الموجودة بـ <code dir=\"ltr\">// @ts-expect-error</code> مع تذكرة، كي يبلغ الكود الجديد المستوى المطلوب فورًا.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>التحويل بإعادة التسمية وإضافة any</span></p>\n<p>إعادة تسمية ملف إلى <code dir=\"ltr\">.ts</code> وإصلاح كل خطأ بـ <code dir=\"ltr\">any</code> أو <code dir=\"ltr\">as any</code> ينتج JavaScript بنكهة TypeScript: يبدو منتهيًا ولا يفحص شيئًا. والأسوأ أنه يُخفي الملف من قائمة “ما زال ينتظر”. تتبّع <code dir=\"ltr\">any</code> كما تتبّع تغطية الاختبارات (قاعدة lint مثل <code dir=\"ltr\">@typescript-eslint/no-explicit-any</code> مضبوطة على التحذير، وعدّاد في CI) واعتبر الرقم مقياسًا للترحيل، لا مجرّد عدد ملفات <code dir=\"ltr\">.ts</code>.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">الحفاظ على السرعة</h2>\n<p>في قاعدة كود كبيرة، تصبح سرعة فحص الأنواع مشكلة في تجربة المطوّرين. قِس قبل أن تخمّن: <code dir=\"ltr\">tsc --noEmit --extendedDiagnostics</code> يطبع زمن الفحص والذاكرة وعدد تجسيدات الأنواع. القفزة المفاجئة في التجسيدات تأتي دائمًا تقريبًا من أداة ثقيلة على مستوى الأنواع، مثل <code dir=\"ltr\">Paths</code> التعاودي من <a href=\"/ar/lesson/advanced-typescript/l-at-3-5\">الأنواع التعاودية ومتى تتوقّف</a>، مطبَّقة على نوع كبير. و<code dir=\"ltr\">--generateTrace &lt;dir&gt;</code> يسجّل أين يذهب الوقت، ملفًا بعد ملف.</p>\n<p>الإصلاحات البنيوية راسخة. فعّل <code dir=\"ltr\">incremental</code> كي لا يُعاد فحص الملفات التي لم تتغيّر. وقسّم المستودع الكبير إلى <strong>مراجع مشاريع (project references)</strong> (<code dir=\"ltr\">composite: true</code> في كل حزمة، و<code dir=\"ltr\">tsc -b</code> في الجذر)، فيُفحص كل جزء مرة واحدة وتُعاد استخدام تصريحاته. وفضّل الـ interfaces التي يمتدّ (<code dir=\"ltr\">extend</code>) بعضها من بعض على السلاسل الطويلة من تقاطعات <code dir=\"ltr\">&amp;</code> لأنواع الكائنات الكبيرة، لأن الـ compiler يخزّن علاقات الـ interfaces مؤقتًا بفعالية أكبر. وعلّق أنواع إرجاع الدوال المُصدَّرة، فهذا يوفّر على الفاحص إعادة استنتاجها ويُبقي مخرجات التصريحات مستقرة.</p>\n<aside class=\"callout callout-note\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg><span><span class=\"callout-kind\">ملاحظة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>TypeScript 7</span></p>\n<p>TypeScript 7.0، الذي صدر في يوليو 2026، نسخة أصلية من الـ compiler مكتوبة بـ Go. يفحص الأنواع عادةً أسرع بـ 8 إلى 12 مرة من 6.0، ويحتفظ بنظام الأنواع نفسه وسلوك سطر الأوامر نفسه، لكنه يُزيل الخيارات التي أهملها 6.0 ولا يأتي بعد بواجهة برمجية، فقد تحتاج الأدوات التي تضمّن الـ compiler إلى البقاء على 6.0 لفترة. أصلح تحذيرات الإهمال في 6.0 أولًا؛ وبعدها تكون تجربة 7.0 في الغالب مجرّد رفع للإصدار.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">إلى أين من هنا</h2>\n<p>استخدمت الأنواع لتصميم الحالات، ووجّهت الـ inference، وكتبت كودًا على مستوى الأنواع باعتدال، وبنيت الأنماط التي تعتمد عليها قواعد الكود الحقيقية، وضبطت الـ compiler ليسندها. والعادة التي يجب أن تحتفظ بها هي تلك التي من الدرس الأول: قبل أن تكتب type، سمِّ الخطأ الذي يمنعه. ثم اختر أبسط type يمنعه. التمرين أدناه يرتّب دليل الترحيل؛ والتقييم النهائي يغطّي الدورة كلها.</p>\n","toc":[{"id":"sec-1","text":"التعايش أولًا","level":2},{"id":"sec-2","text":"افحص JavaScript قبل تحويلها","level":2},{"id":"sec-3","text":"حوّل من الأوراق نحو الداخل","level":2},{"id":"sec-4","text":"الحفاظ على السرعة","level":2},{"id":"sec-5","text":"إلى أين من هنا","level":2}],"takeaways":["رحّل تدريجيًا باستخدام <code dir=\"ltr\">allowJs</code>، كي تعيش ملفات JavaScript وTypeScript جنبًا إلى جنب ويُنشر التطبيق كل يوم.","<code dir=\"ltr\">// @ts-check</code> مع أنواع JSDoc يعطي ملف JavaScript فحصًا حقيقيًا قبل أن تعيد تسميته.","حوّل وحدات الأوراق أولًا واعمل نحو نقاط الدخول، كي يستورد كل ملف محوَّل حديثًا كودًا محدَّد الأنواع.","حدّد أنواع الحدود (استجابات الـ API ونماذج المجال) مبكرًا؛ فهي تعطي أكبر حماية مقابل كل ساعة عمل.","قِس فحص الأنواع بـ <code dir=\"ltr\">--extendedDiagnostics</code>، واستخدم البناء التدريجي ومراجع المشاريع، وأبقِ كود مستوى الأنواع الثقيل خارج المسارات الساخنة."],"further":[{"title":"Migrating from JavaScript","url":"https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html"},{"title":"JSDoc Reference","url":"https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html"},{"title":"Project References","url":"https://www.typescriptlang.org/docs/handbook/project-references.html"},{"title":"Type Checking JavaScript Files","url":"https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html"}],"quiz":[{"q":"<p>تبدأ ترحيل تطبيق JavaScript من 1,500 ملف. أيّ خطوة أولى تُبقي التطبيق قابلًا للنشر؟</p>","options":[{"text":"أنشئ branch طويل العمر باسم <code dir=\"ltr\">typescript</code> وأعِد تسمية كل ملف إلى <code dir=\"ltr\">.ts</code> هناك.","why":"الـ branch الضخم دفعة واحدة يبتعد عن <code dir=\"ltr\">main</code> يوميًا ويُدمج في جدار من التعارضات والأخطاء. والفرق التي تجرّبه كثيرًا ما تتخلّى عنه."},{"text":"أعِد تسمية كل ملف إلى <code dir=\"ltr\">.ts</code> على <code dir=\"ltr\">main</code> وأضف <code dir=\"ltr\">// @ts-nocheck</code> إلى كلٍّ منها.","why":"هذا ينشئ آلاف الملفات التي تبدو محدَّدة الأنواع ولا تفحص شيئًا، وإزالة التوجيه لاحقًا هي مشكلة “الدفعة الواحدة” نفسها."},{"text":"انتظر حتى يجد الفريق وقتًا لتحويل كل شيء في sprint واحد.","why":"ذلك الـ sprint لا يأتي أبدًا لقاعدة كود بهذا الحجم؛ العمل التدريجي هو النهج الوحيد الذي يكتمل."},{"text":"أضف tsconfig فيه <code dir=\"ltr\">allowJs</code>، وشغّل <code dir=\"ltr\">tsc --noEmit</code> في CI، وحوّل الملفات واحدًا تلو الآخر على <code dir=\"ltr\">main</code>.","why":"صحيح. تتعايش JavaScript وTypeScript، ويُنشر كل pull request، ولا تتحرّك النسبة المحوَّلة إلا صعودًا."}],"answer":3},{"q":"<p>بأيّ ترتيب يجب أن تحوّل الوحدات؟</p>","options":[{"text":"نقاط الدخول أولًا، كي يكون الكود الأكثر ظهورًا محدَّد الأنواع.","why":"نقاط الدخول تستورد كل شيء آخر؛ وإن حُوّلت مبكرًا فستستورد وحدات بلا أنواع وتحصل في الغالب على <code dir=\"ltr\">any</code>."},{"text":"وحدات الأوراق (التي لا تستورد شيئًا داخليًا) أولًا، ثم الوحدات التي تعتمد عليها.","why":"صحيح. حينها يستورد كل ملف محوَّل كودًا محدَّد الأنواع أصلًا، فتكون أنواعه حقيقية منذ اليوم الأول."},{"text":"أبجديًا، كي يسهل تتبّع التقدّم.","why":"أسماء الملفات لا تقول شيئًا عن الاعتماديات؛ ستحوّل كثيرًا ملفًا قبل الكود الذي يعتمد عليه."}],"answer":1},{"q":"<p>يُظهر <code dir=\"ltr\">tsc --extendedDiagnostics</code> تسعة ملايين تجسيد للأنواع، معظمها من ملف واحد. ما السبب المرجّح؟</p>","options":[{"text":"أداة ثقيلة على مستوى الأنواع (تعاود عميق، أو unions كبيرة من template literals) مطبَّقة على نوع كبير.","why":"صحيح. أعداد التجسيد تنفجر مع الأنواع التعاودية والتركيبية؛ بسّط ذلك النوع أو ضيّقه أولًا."},{"text":"تعليقات كثيرة جدًا في ذلك الملف.","why":"فاحص الأنواع يتخطّى التعليقات؛ لا تكلّف شيئًا يمكن قياسه."},{"text":"تعليقات نوعية بـ <code dir=\"ltr\">any</code> غير كافية.","why":"إضافة <code dir=\"ltr\">any</code> تُخفي المشكلة بإزالة الفحص، وهذا ليس إصلاحًا."}],"answer":0},{"q":"<p>كيف يساعد <code dir=\"ltr\">// @ts-check</code> أعلى ملف <code dir=\"ltr\">.js</code> في الترحيل؟</p>","options":[{"text":"يحوّل الملف إلى TypeScript تلقائيًا.","why":"يبقى الملف JavaScript؛ لا شيء يُعاد تسميته أو كتابته."},{"text":"يجعل <code dir=\"ltr\">tsc</code> يولّد ملف <code dir=\"ltr\">.d.ts</code> لتلك الوحدة.","why":"مخرجات التصريحات تعتمد على إعدادات <code dir=\"ltr\">declaration</code>، لا على <code dir=\"ltr\">@ts-check</code>."},{"text":"يفحص أنواع ملف JavaScript ذلك، مستخدمًا تعليقات JSDoc للأنواع، فتستطيع إيجاد الأخطاء قبل إعادة تسميته.","why":"صحيح. تعليقات JSDoc مثل <code dir=\"ltr\">@param {Order} order</code> أنواع حقيقية بالنسبة للفاحص، دون تغييرات في البناء."}],"answer":2}],"exercise":{"kind":"order","items":["add-tsconfig","check-boundaries","convert-leaves","convert-inward","drop-allowjs","ratchet-flags"],"title":"رتّب دليل الترحيل","prompt":"<p>تطبيق إدارة الطلبات في Cartwheel مكوَّن من 1,500 ملف JavaScript ويجب أن يستمر في نشر التحديثات كل يوم. رتّب خطوات الترحيل هذه بالترتيب الذي يوصي به الدرس، من أول pull request إلى آخره.</p>\n","hints":["الخطوة الأولى تمامًا لا تغيّر شيئًا في طريقة بناء التطبيق أو تشغيله.","يمكنك الحصول على فحص الأنواع في ملفات JavaScript قبل إعادة تسمية أيٍّ منها.","يجب أن تستورد الملفات المحوَّلة كودًا محدَّد الأنواع أصلًا، وتأتي خيارات الصرامة الإضافية حين يصير كل شيء TypeScript."],"explanation":"<p>أولًا، tsconfig فيه <code dir=\"ltr\">allowJs</code> و<code dir=\"ltr\">noEmit</code>، يُفحص في CI، يتيح للغتين التعايش دون المساس بالبناء. بعد ذلك، <code dir=\"ltr\">// @ts-check</code> وJSDoc على الحدود (عميل الـ API ونماذج المجال) يعطيان أكبر حماية قبل أي إعادة تسمية. ثم حوّل وحدات الأوراق، واعمل نحو الداخل، كي يستورد كل ملف محوَّل كودًا محدَّد الأنواع. وحين لا يبقى أي مصدر <code dir=\"ltr\">.js</code>، أسقط <code dir=\"ltr\">allowJs</code>. وأخيرًا، شدّ الخيارات الإضافية مثل <code dir=\"ltr\">noUncheckedIndexedAccess</code> واحدًا تلو الآخر، مُخفيًا الأخطاء الموجودة بـ <code dir=\"ltr\">@ts-expect-error</code> مع تذكرة كي يبلغ الكود الجديد المستوى الأعلى فورًا.</p>\n","checkLabels":{},"itemText":{"add-tsconfig":"أضف tsconfig فيه <code dir=\"ltr\">allowJs</code> و<code dir=\"ltr\">noEmit</code> و<code dir=\"ltr\">strict</code>، وشغّل <code dir=\"ltr\">tsc --noEmit</code> في CI بجوار البناء الموجود","check-boundaries":"أضف <code dir=\"ltr\">// @ts-check</code> وأنواع JSDoc إلى ملفات عميل الـ API ونماذج المجال","convert-leaves":"أعِد تسمية وحدات الأوراق (الدوال المساعدة التي لا تستورد شيئًا داخليًا) من <code dir=\"ltr\">.js</code> إلى <code dir=\"ltr\">.ts</code>","convert-inward":"حوّل الوحدات التي تعتمد على وحدات محوَّلة أصلًا، عاملًا نحو نقاط الدخول","drop-allowjs":"أزِل <code dir=\"ltr\">allowJs</code> حين لا يبقى أي ملف مصدر <code dir=\"ltr\">.js</code>","ratchet-flags":"فعّل <code dir=\"ltr\">noUncheckedIndexedAccess</code> والخيارات الإضافية الأخرى واحدًا تلو الآخر، معلّمًا الأخطاء القديمة بـ <code dir=\"ltr\">@ts-expect-error</code>"}},"words":737,"readMinutes":4}