{"id":"l-at-3-1","courseId":"advanced-typescript","lang":"ar","kind":"lesson","summary":"اشتقّ الأنواع من قيم وقت التشغيل ومن أنواع أخرى باستخدام typeof وkeyof والوصول بالفهرس، كي يسري التغيير في مكان واحد عبر قاعدة الكود بدل أن تتباعد النُّسخ.","html":"<p>مناطق الشحن في Cartwheel موجودة في كائن إعداد. النسخة الأولى من الكود وصفتها مرتين: مرة كبيانات، ومرة كأنواع.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">TypeScript</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:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> SHIPPING_ZONES</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  domestic: { countries: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'US'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'CA'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">499</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  gulf: { countries: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'AE'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'SA'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1299</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  europe: { countries: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'GB'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'DE'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">999</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">};</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Zone</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'domestic'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> |</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'gulf'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> |</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'europe'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// written by hand</span></span></code></pre></div>\n<p>بعد ستة أشهر أضاف أحدهم منطقة <code dir=\"ltr\">levant</code> إلى الكائن ونسي النوع. لم يفشل شيء في الترجمة؛ كل ما في الأمر أن المنطقة الجديدة لم يكن ممكنًا اختيارها في أي مكان. كل نسخة مكتوبة يدويًا من حقيقةٍ ما هي نسخة قابلة للتباعد. هذا الدرس يغطّي عوامل الأنواع (type operators) الثلاثة التي تتيح لك كتابة الحقيقة مرة واحدة و<strong>اشتقاق</strong> الباقي.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">typeof: من القيم إلى الأنواع</h2>\n<p>في موضع النوع، تأخذ <code dir=\"ltr\">typeof</code> <em>قيمة</em> وتعطيك النوع الذي استنتجه TypeScript لها. إنها ليست العامل <code dir=\"ltr\">typeof</code> في JavaScript (الذي يُعيد نصوصًا مثل <code dir=\"ltr\">'object'</code> وقت التشغيل)؛ إنها لا توجد إلا وقت الترجمة.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">TypeScript</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:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> SHIPPING_ZONES</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  domestic: { countries: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'US'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'CA'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">499</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  gulf: { countries: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'AE'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'SA'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1299</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  europe: { countries: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'GB'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'DE'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">999</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> const</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ShippingZones</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> typeof</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> SHIPPING_ZONES</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// { readonly domestic: { readonly countries: readonly ['US', 'CA']; readonly feeCents: 499 }; … }</span></span></code></pre></div>\n<p><code dir=\"ltr\">as const</code> مهمة هنا، كما كانت في <a href=\"/ar/lesson/advanced-typescript/l-at-1-5\">الـ Literal Types و as const و satisfies</a>. دونها سيكون <code dir=\"ltr\">countries</code> من نوع <code dir=\"ltr\">string[]</code> و<code dir=\"ltr\">feeCents</code> من نوع <code dir=\"ltr\">number</code>، وستفقد التفاصيل الجديرة بالاشتقاق.</p>\n<p>تعمل <code dir=\"ltr\">typeof</code> مع الدوال أيضًا: <code dir=\"ltr\">typeof formatPrice</code> هو التوقيع الكامل للدالة، ويمكنك تمريره إلى utility types (أنواع مساعدة) مثل <code dir=\"ltr\">ReturnType&lt;typeof formatPrice&gt;</code>. ستبني <code dir=\"ltr\">ReturnType</code> بنفسك بعد درسين.</p>\n<p>متى يجب أن تكون القيمة هي المصدر والنوع مشتقًا، لا العكس؟ استخدم <code dir=\"ltr\">typeof</code> حين تكون البيانات <em>هي</em> التعريف: جداول البحث، وإعدادات المناطق، وقوائم الخيارات التي تُعرض أيضًا في الواجهة. واكتب النوع أولًا حين يكون الشكل عقدًا مع طرف آخر، مثل حمولة API، وافحص القيم مقابله.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 230\" role=\"img\" aria-labelledby=\"l-at-3-1-t1\"><defs><marker id=\"arrow-l-at-3-1-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-3-1-t1\">عالمان. في عالم القيم يقع الكائن SHIPPING_ZONES. تنقله typeof إلى عالم الأنواع كنوع. ومن هناك، تنتج keyof الـ union من أسماء المناطق، والوصول بالفهرس بتلك الأسماء ينتج الـ union من إعدادات المناطق، والفهرسة الأعمق تعطي الرسوم.</title>\n  <rect class=\"d-box\" x=\"10\" y=\"20\" width=\"200\" height=\"190\" rx=\"14\"/>\n  <text class=\"d-label-strong\" x=\"110\" y=\"48\" text-anchor=\"middle\">القيم (وقت التشغيل)</text>\n  <rect class=\"d-box-accent\" x=\"30\" y=\"90\" width=\"160\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"110\" y=\"120\" text-anchor=\"middle\">SHIPPING_ZONES</text>\n  <path class=\"d-arrow\" d=\"M190 115 L268 115\" marker-end=\"url(#arrow-l-at-3-1-1)\"/>\n  <text class=\"d-code\" x=\"229\" y=\"105\" text-anchor=\"middle\">typeof</text>\n  <rect class=\"d-box\" x=\"272\" y=\"20\" width=\"418\" height=\"190\" rx=\"14\"/>\n  <text class=\"d-label-strong\" x=\"481\" y=\"48\" text-anchor=\"middle\">الأنواع (وقت الترجمة)</text>\n  <rect class=\"d-box-primary\" x=\"292\" y=\"90\" width=\"120\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"352\" y=\"120\" text-anchor=\"middle\">Zones</text>\n  <path class=\"d-arrow\" d=\"M412 104 L462 80\" marker-end=\"url(#arrow-l-at-3-1-1)\"/>\n  <rect class=\"d-box-success\" x=\"466\" y=\"58\" width=\"210\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"571\" y=\"83\" text-anchor=\"middle\">keyof → 'domestic' | …</text>\n  <path class=\"d-arrow\" d=\"M412 126 L462 150\" marker-end=\"url(#arrow-l-at-3-1-1)\"/>\n  <rect class=\"d-box-success\" x=\"466\" y=\"132\" width=\"210\" height=\"40\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"571\" y=\"157\" text-anchor=\"middle\">[K]['feeCents'] → 499 | …</text>\n</svg><figcaption>typeof تعبر من القيم إلى الأنواع؛ وkeyof والفهرسة تعملان على الأنواع</figcaption></figure>\n<h2 id=\"sec-2\" tabindex=\"-1\">keyof: أسماء الخصائص</h2>\n<p><code dir=\"ltr\">keyof T</code> هو الـ union من أسماء خصائص <code dir=\"ltr\">T</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">TypeScript</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:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Zone</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> keyof</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> typeof</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> SHIPPING_ZONES</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 'domestic' | 'gulf' | 'europe'</span></span></code></pre></div>\n<p>أضف <code dir=\"ltr\">levant</code> إلى الكائن فيتحدّث <code dir=\"ltr\">Zone</code> من تلقاء نفسه. وهذا هو المقصود كله.</p>\n<p>تفصيلان يستحقان المعرفة. أولًا، مع نوع فيه index signature مثل <code dir=\"ltr\">Record&lt;string, number&gt;</code>، تعطي <code dir=\"ltr\">keyof</code> النوع <code dir=\"ltr\">string</code> (و<code dir=\"ltr\">string | number</code> مع <code dir=\"ltr\">{ [k: string]: … }</code>، لأن JavaScript تحوّل المفاتيح الرقمية إلى نصوص). ثانيًا، <code dir=\"ltr\">keyof</code> على union تعطي فقط المفاتيح التي يملكها <strong>كل</strong> عضو. <code dir=\"ltr\">keyof (ShippedOrder | CancelledOrder)</code> هو <code dir=\"ltr\">'id' | 'status'</code>، لأن قراءة <code dir=\"ltr\">trackingNumber</code> من شيء قد يكون طلبًا ملغى ليست آمنة.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">الوصول بالفهرس: نوع الخاصية</h2>\n<p><code dir=\"ltr\">T[K]</code> تبحث عن نوع الخاصية <code dir=\"ltr\">K</code> في <code dir=\"ltr\">T</code>، بصياغة الأقواس المربّعة نفسها المستخدمة للوصول إلى الخصائص وقت التشغيل:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">TypeScript</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:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> GulfZone</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ShippingZones</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'gulf'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];             </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// { readonly countries: …; readonly feeCents: 1299 }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> GulfFee</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ShippingZones</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'gulf'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">][</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'feeCents'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 1299</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ZoneConfig</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ShippingZones</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#953800;--shiki-dark:#FFA657\">Zone</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];             </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// union of all three configs</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> FeeCents</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ShippingZones</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#953800;--shiki-dark:#FFA657\">Zone</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">][</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'feeCents'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 499 | 1299 | 999</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ShippingCountry</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ShippingZones</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#953800;--shiki-dark:#FFA657\">Zone</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">][</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'countries'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">][</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]; </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 'US' | 'CA' | 'AE' | …</span></span></code></pre></div>\n<p>الفهرسة بـ union تعطي union من النتائج، فيكون <code dir=\"ltr\">ShippingZones[Zone]</code> هو “نوع قيمة أي منطقة”. والتعبير <code dir=\"ltr\">(typeof X)[keyof typeof X]</code> هو طريقتك للحصول على الـ union من قيم الكائن، ويتكرّر بما يكفي لأن تسمّيه قواعد كود كثيرة <code dir=\"ltr\">ValueOf&lt;T&gt;</code>.</p>\n<p>المصفوفات والـ tuples تُفهرس بـ <code dir=\"ltr\">number</code>: <code dir=\"ltr\">T[number]</code> هو نوع العنصر. هذا هو <code dir=\"ltr\">(typeof COUNTRIES)[number]</code> الذي استخدمته في القسم 1، وهو أيضًا السطر الأخير أعلاه، الذي يحفر عبر مستويين من الكائنات ومصفوفة واحدة ليجمع كل دولة في كل منطقة.</p>\n<p>الحيلة نفسها تعمل على أنواع API لم تكتبها أنت. إن أعطاك عميل مولَّد نوعًا كبيرًا اسمه <code dir=\"ltr\">CheckoutResponse</code>، يمكنك تسمية نوع البند بـ <code dir=\"ltr\">CheckoutResponse['lines'][number]</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>استخدام الوصول بالنقطة في الأنواع</span></p>\n<p><code dir=\"ltr\">CheckoutResponse.lines</code> في موضع النوع لا تعني “نوع الخاصية <code dir=\"ltr\">lines</code>”. يقرأ TypeScript النقطة كبحث في namespace ويبلّغ عن <em>Cannot access ‘CheckoutResponse.lines’ because ‘CheckoutResponse’ is a type, but not a namespace</em>، مقترحًا الإصلاح مشكورًا. الأنواع تستخدم الأقواس المربّعة دائمًا: <code dir=\"ltr\">CheckoutResponse['lines']</code>.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">لماذا تُعيد Object.keys النوع string[]</h2>\n<p>عاجلًا أو آجلًا ستكتب هذا وتنزعج:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">TypeScript</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:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> zone</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> of</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Object.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">keys</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">SHIPPING_ZONES</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)) {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  SHIPPING_ZONES</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[zone]; </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Error: 'string' can't be used to index type …</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>تُعيد <code dir=\"ltr\">Object.keys</code> النوع <code dir=\"ltr\">string[]</code>، لا <code dir=\"ltr\">Zone[]</code>، وهذا مقصود. تذكّر التنميط البنيوي: القيمة التي نوعها <code dir=\"ltr\">{ a: number }</code> قد تكون كائنًا فيه عشر خصائص أخرى وقت التشغيل. لو وعدت <code dir=\"ltr\">Object.keys</code> بـ <code dir=\"ltr\">(keyof T)[]</code>، لكانت تكذب بشأن تلك العشر.</p>\n<p>مع كائن <code dir=\"ltr\">const</code> عرّفته بنفسك، لا سبيل لتسلّل مفاتيح إضافية إليه، يكون التحويل المحلّي معقولًا وصادقًا: <code dir=\"ltr\">Object.keys(SHIPPING_ZONES) as Zone[]</code>. أما مع الكائنات القادمة من مكان آخر، فأبقِ <code dir=\"ltr\">string</code> وافحص كل مفتاح.</p>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>اشتقّ في اتجاه واحد</span></p>\n<p>اختر مصدر حقيقة واحدًا واشتقّ منه في اتجاه واحد. إمّا أن يكون كائن وقت التشغيل هو المصدر والأنواع تأتي من <code dir=\"ltr\">typeof</code>، أو أن يكون الـ type هو المصدر وقيم وقت التشغيل تُفحص بـ <code dir=\"ltr\">satisfies</code>. الخلط بين الاثنين (بعض الحقول مكتوبة يدويًا وبعضها مشتق) هو الطريق الذي يعود منه التباعد.</p>\n</aside>\n<p>في التمرين ستشتقّ خمسة أنواع من إعدادات الشحن في Cartwheel ومن استجابة الـ API. والدرس القادم يذهب خطوة أبعد: بدل <em>قراءة</em> الخصائص من نوع، ستـ<em>حوّل</em> كل خصائص النوع دفعة واحدة.</p>\n","toc":[{"id":"sec-1","text":"typeof: من القيم إلى الأنواع","level":2},{"id":"sec-2","text":"keyof: أسماء الخصائص","level":2},{"id":"sec-3","text":"الوصول بالفهرس: نوع الخاصية","level":2},{"id":"sec-4","text":"لماذا تُعيد Object.keys النوع string[]","level":2}],"takeaways":["في موضع النوع، تعطي <code dir=\"ltr\">typeof value</code> النوع الذي استنتجه TypeScript لتلك القيمة، فتكون جسرًا من كود وقت التشغيل إلى نظام الأنواع.","<code dir=\"ltr\">keyof T</code> هو الـ union من أسماء خصائص <code dir=\"ltr\">T</code>، و<code dir=\"ltr\">T[K]</code> هو نوع الخاصية (أو الخصائص) التي يسمّيها <code dir=\"ltr\">K</code>.","<code dir=\"ltr\">(typeof CONFIG)[keyof typeof CONFIG]</code> يعطي الـ union من أنواع قيم الكائن، و<code dir=\"ltr\">T[number]</code> يعطي نوع عنصر المصفوفة.","تُعيد <code dir=\"ltr\">Object.keys</code> النوع <code dir=\"ltr\">string[]</code> عن قصد: التنميط البنيوي يعني أن الكائن قد يحمل مفاتيح أكثر مما يسرده نوعه."],"further":[{"title":"Keyof Type Operator","url":"https://www.typescriptlang.org/docs/handbook/2/keyof-types.html"},{"title":"Typeof Type Operator","url":"https://www.typescriptlang.org/docs/handbook/2/typeof-types.html"},{"title":"Indexed Access Types","url":"https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html"}],"quiz":[{"q":"<p>ما هو <code dir=\"ltr\">Zone</code>؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"ts\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">TypeScript</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:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> SHIPPING_ZONES</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  domestic: { feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">499</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  gulf: { feeCents: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1299</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">as</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> const</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Zone</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> keyof</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> typeof</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> SHIPPING_ZONES</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">'domestic' | 'gulf'</code>","why":"صحيح. <code dir=\"ltr\">typeof</code> تحوّل القيمة إلى نوعها، و<code dir=\"ltr\">keyof</code> تأخذ الـ union من أسماء خصائصها."},{"text":"<code dir=\"ltr\">string</code>","why":"<code dir=\"ltr\">keyof</code> على نوع كائن بخصائص معروفة يعطي تلك الأسماء بالضبط، لا <code dir=\"ltr\">string</code>. لا تحصل على <code dir=\"ltr\">string</code> إلا من index signature."},{"text":"<code dir=\"ltr\">{ feeCents: 499 } | { feeCents: 1299 }</code>","why":"هذا هو الـ union من القيم، وتحصل عليه بـ <code dir=\"ltr\">(typeof SHIPPING_ZONES)[Zone]</code>."},{"text":"خطأ، لأن <code dir=\"ltr\">keyof</code> تحتاج نوعًا لا قيمة.","why":"<code dir=\"ltr\">typeof</code> في موضع النوع تحوّل القيمة أولًا، فتتلقّى <code dir=\"ltr\">keyof</code> نوعًا فعلًا."}],"answer":0},{"q":"<p><code dir=\"ltr\">type CheckoutResponse = { lines: { sku: string; qty: number }[] }</code>. أيّ نوع يساوي <code dir=\"ltr\">{ sku: string; qty: number }</code>؟</p>","options":[{"text":"<code dir=\"ltr\">CheckoutResponse.lines[0]</code>","why":"الوصول بالنقطة غير موجود في مواضع الأنواع؛ يقرأ TypeScript <code dir=\"ltr\">CheckoutResponse.lines</code> كبحث في namespace فيفشل."},{"text":"<code dir=\"ltr\">CheckoutResponse['lines']</code>","why":"هذا نوع المصفوفة كاملة، <code dir=\"ltr\">{ sku: string; qty: number }[]</code>، لا عنصر واحد."},{"text":"<code dir=\"ltr\">keyof CheckoutResponse['lines']</code>","why":"هذا يعطي مفاتيح المصفوفة (<code dir=\"ltr\">number</code> و<code dir=\"ltr\">'length'</code> و<code dir=\"ltr\">'map'</code> …)، لا عناصرها."},{"text":"<code dir=\"ltr\">CheckoutResponse['lines'][number]</code>","why":"صحيح. فهرسة نوع مصفوفة بـ <code dir=\"ltr\">number</code> تعطي نوع عنصرها."}],"answer":3},{"q":"<p>ما هو <code dir=\"ltr\">keyof (ShippedOrder | CancelledOrder)</code> حين يملك كلاهما <code dir=\"ltr\">id</code> و<code dir=\"ltr\">status</code>، ولا يملك <code dir=\"ltr\">trackingNumber</code> إلا <code dir=\"ltr\">ShippedOrder</code>، ولا يملك <code dir=\"ltr\">cancelReason</code> إلا <code dir=\"ltr\">CancelledOrder</code>؟</p>","options":[{"text":"<code dir=\"ltr\">'id' | 'status' | 'trackingNumber' | 'cancelReason'</code>","why":"هذا سيتيح لك قراءة <code dir=\"ltr\">trackingNumber</code> من قيمة قد تكون طلبًا ملغى، وهذا غير آمن."},{"text":"<code dir=\"ltr\">'id' | 'status'</code>","why":"صحيح. مفتاح الـ union يجب أن يكون مفتاحًا في كل عضو، فلا يبقى إلا الخصائص المشتركة."},{"text":"<code dir=\"ltr\">never</code>، لأن النوعين مختلفان","why":"العضوان يشتركان في <code dir=\"ltr\">id</code> و<code dir=\"ltr\">status</code>، فتنجو تلك المفاتيح."}],"answer":1},{"q":"<p>لماذا تُعيد <code dir=\"ltr\">Object.keys(order)</code> النوع <code dir=\"ltr\">string[]</code> بدل <code dir=\"ltr\">(keyof Order)[]</code>؟</p>","options":[{"text":"لأن فريق TypeScript لم يتفرّغ لتحديد نوعها بعد.","why":"إنه خيار مقصود ينبع من طريقة عمل نظام الأنواع، لا سهو."},{"text":"لأن <code dir=\"ltr\">keyof</code> غير متاحة في ملفات الـ lib.","why":"ملفات الـ lib تستخدم <code dir=\"ltr\">keyof</code> في كل مكان؛ هذا ليس قيدًا تقنيًا."},{"text":"لأن القيمة من نوع <code dir=\"ltr\">Order</code> قد تحمل خصائص إضافية وقت التشغيل لا يسردها النوع.","why":"صحيح. التنميط البنيوي يسمح لـ <code dir=\"ltr\">DetailedOrder</code> بالمرور كـ <code dir=\"ltr\">Order</code>، فتظهر مفاتيحه الإضافية وقت التشغيل."}],"answer":2}],"exercise":{"kind":"web","mode":"ts","starter":{"ts":"const SHIPPING_ZONES = {\n  domestic: { countries: ['US', 'CA'], feeCents: 499 },\n  gulf: { countries: ['AE', 'SA'], feeCents: 1299 },\n  levant: { countries: ['EG', 'JO'], feeCents: 899 },\n  europe: { countries: ['GB', 'DE'], feeCents: 999 },\n} as const;\n\n// Shape of the checkout API response (generated from the backend's schema).\ntype CheckoutResponse = {\n  order: { id: number; status: 'processing' | 'shipped'; totalCents: number };\n  lines: { sku: string; quantity: number; unitPriceCents: number }[];\n};\n\ntype Zone = string;\ntype ShippingCountry = string;\ntype FeeCents = number;\ntype LineItem = unknown;\ntype CheckoutStatus = string;\n\nfunction zoneFor(country: ShippingCountry): Zone {\n  for (const [zone, config] of Object.entries(SHIPPING_ZONES)) {\n    if ((config.countries as readonly string[]).includes(country)) return zone;\n  }\n  throw new Error(`No shipping zone for ${country}`);\n}\n\nfunction feeFor(country: ShippingCountry): FeeCents {\n  return SHIPPING_ZONES[zoneFor(country)].feeCents;\n}\n\n// ---- Type tests (do not edit) ----\ntype T1 = Expect<Equal<Zone, 'domestic' | 'gulf' | 'levant' | 'europe'>>;\ntype T2 = Expect<Equal<ShippingCountry, 'US' | 'CA' | 'AE' | 'SA' | 'EG' | 'JO' | 'GB' | 'DE'>>;\ntype T3 = Expect<Equal<FeeCents, 499 | 1299 | 899 | 999>>;\ntype T4 = Expect<Equal<LineItem, { sku: string; quantity: number; unitPriceCents: number }>>;\ntype T5 = Expect<Equal<CheckoutStatus, 'processing' | 'shipped'>>;\n\nfunction typeTests() {\n  // @ts-expect-error: France is not in any shipping zone\n  zoneFor('FR');\n}\n\nconsole.log(zoneFor('JO'), feeFor('SA'));\n"},"solution":{"ts":"const SHIPPING_ZONES = {\n  domestic: { countries: ['US', 'CA'], feeCents: 499 },\n  gulf: { countries: ['AE', 'SA'], feeCents: 1299 },\n  levant: { countries: ['EG', 'JO'], feeCents: 899 },\n  europe: { countries: ['GB', 'DE'], feeCents: 999 },\n} as const;\n\n// Shape of the checkout API response (generated from the backend's schema).\ntype CheckoutResponse = {\n  order: { id: number; status: 'processing' | 'shipped'; totalCents: number };\n  lines: { sku: string; quantity: number; unitPriceCents: number }[];\n};\n\ntype ShippingZones = typeof SHIPPING_ZONES;\ntype Zone = keyof ShippingZones;\ntype ShippingCountry = ShippingZones[Zone]['countries'][number];\ntype FeeCents = ShippingZones[Zone]['feeCents'];\ntype LineItem = CheckoutResponse['lines'][number];\ntype CheckoutStatus = CheckoutResponse['order']['status'];\n\nfunction zoneFor(country: ShippingCountry): Zone {\n  // SHIPPING_ZONES is our own const object, so it has no extra keys.\n  for (const zone of Object.keys(SHIPPING_ZONES) as Zone[]) {\n    if ((SHIPPING_ZONES[zone].countries as readonly string[]).includes(country)) return zone;\n  }\n  throw new Error(`No shipping zone for ${country}`);\n}\n\nfunction feeFor(country: ShippingCountry): FeeCents {\n  return SHIPPING_ZONES[zoneFor(country)].feeCents;\n}\n\n// ---- Type tests (do not edit) ----\ntype T1 = Expect<Equal<Zone, 'domestic' | 'gulf' | 'levant' | 'europe'>>;\ntype T2 = Expect<Equal<ShippingCountry, 'US' | 'CA' | 'AE' | 'SA' | 'EG' | 'JO' | 'GB' | 'DE'>>;\ntype T3 = Expect<Equal<FeeCents, 499 | 1299 | 899 | 999>>;\ntype T4 = Expect<Equal<LineItem, { sku: string; quantity: number; unitPriceCents: number }>>;\ntype T5 = Expect<Equal<CheckoutStatus, 'processing' | 'shipped'>>;\n\nfunction typeTests() {\n  // @ts-expect-error: France is not in any shipping zone\n  zoneFor('FR');\n}\n\nconsole.log(zoneFor('JO'), feeFor('SA'));\n"},"checks":[{"id":"types","typecheck":true},{"id":"zone","test":"return zoneFor('JO') === 'levant' && zoneFor('CA') === 'domestic';"},{"id":"fee","test":"return feeFor('SA') === 1299 && feeFor('GB') === 999;"}],"title":"اشتقّ أنواع الشحن","prompt":"<p>استبدل الأنواع الخمسة المكتوبة يدويًا بأنواع <strong>مشتقّة</strong> من <code dir=\"ltr\">SHIPPING_ZONES</code> و<code dir=\"ltr\">CheckoutResponse</code>، بحيث تتحدّث تلقائيًا عند إضافة منطقة أو تغيير نوع الـ API:</p>\n<ul>\n<li><code dir=\"ltr\">Zone</code>: أسماء المناطق.</li>\n<li><code dir=\"ltr\">ShippingCountry</code>: كل دولة في كل منطقة.</li>\n<li><code dir=\"ltr\">FeeCents</code>: رسوم كل منطقة، كأرقام حرفية.</li>\n<li><code dir=\"ltr\">LineItem</code>: عنصر واحد من <code dir=\"ltr\">CheckoutResponse['lines']</code>.</li>\n<li><code dir=\"ltr\">CheckoutStatus</code>: نوع <code dir=\"ltr\">status</code> في الطلب.</li>\n</ul>\n<p>ثم اجعل <code dir=\"ltr\">zoneFor</code> و<code dir=\"ltr\">feeFor</code> تمرّان في الترجمة بالأنواع الجديدة (بلا <code dir=\"ltr\">any</code>).</p>\n","hints":["ابدأ من <code dir=\"ltr\">typeof SHIPPING_ZONES</code>؛ و<code dir=\"ltr\">keyof</code> عليه تعطي أسماء المناطق.","افهرس بالـ union من أسماء المناطق لتصل إلى كل الإعدادات دفعة واحدة: <code dir=\"ltr\">ShippingZones[Zone]['feeCents']</code>. والمصفوفات تُفهرس بـ <code dir=\"ltr\">number</code>.","<code dir=\"ltr\">Object.entries</code> و<code dir=\"ltr\">Object.keys</code> تعطيانك مفاتيح من نوع <code dir=\"ltr\">string</code>. ولأن <code dir=\"ltr\">SHIPPING_ZONES</code> كائنك الخاص المعرَّف بـ <code dir=\"ltr\">as const</code>، فإن <code dir=\"ltr\">Object.keys(SHIPPING_ZONES) as Zone[]</code> تحويل محلّي صادق."],"explanation":"<p>تُدخل <code dir=\"ltr\">typeof</code> كائن الإعداد إلى عالم الأنواع، وتسرد <code dir=\"ltr\">keyof</code> أسماء مناطقه، والوصول بالفهرس بالـ union المسمّى <code dir=\"ltr\">Zone</code> يصل إلى كل المناطق دفعة واحدة، فيجمع <code dir=\"ltr\">ShippingZones[Zone]['countries'][number]</code> كل الدول. ويسمّي <code dir=\"ltr\">CheckoutResponse['lines'][number]</code> نوع البند دون نسخ حقوله. تُعيد <code dir=\"ltr\">Object.keys</code> النوع <code dir=\"ltr\">string[]</code> لأن الكائنات قد تحمل مفاتيح إضافية وقت التشغيل؛ أما مع كائن const عرّفته أنت، فالتحويل إلى <code dir=\"ltr\">Zone[]</code> آمن، ويجلس التحويل بجوار تعليق يشرح السبب.</p>\n","checkLabels":{"types":"الملف يجتاز فحص الأنواع بلا أي خطأ، بما فيها كل اختبارات الأنواع","zone":"<code dir=\"ltr\">zoneFor</code> تجد المنطقة الصحيحة لدولة ما","fee":"<code dir=\"ltr\">feeFor</code> تُعيد رسوم المنطقة"}},"words":651,"readMinutes":4}