Menu
flag Ar iconالعربيةdown icon

الحلقات في TypeScript: for وforEach وfor...of وfor...in

كل طرق التكرار في TypeScript والأنواع التي تعطيها كل منها: حلقة for الكلاسيكية، وfor...of على المصفوفات وMaps والنصوص، وfor...in ومفاتيحها النصية، وforEach (بلا break ولا await)، والحلقات على مفاتيح الكائنات بأنواع صحيحة، وwhile.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

في TypeScript الحلقات نفسها الموجودة في JavaScript: for وfor...of وfor...in وwhile وdo...while، ودالة المصفوفات forEach. وما تضيفه TypeScript هو الأنواع: متغير الحلقة في for...of يأخذ نوع العنصر، ومفتاح for...in دائمًا string.

في معظم حلقات المصفوفات، for...of هو الخيار الأول: يُقرأ بوضوح، ويدعم break وcontinue وawait، ولا يحتاج إلى أي تصريح بالأنواع.

حلقة for الكلاسيكية

for (init; condition; update) هي الحلقة المناسبة للعدّ، أو للتقدم بخطوة أكبر من واحد، أو للمرور بترتيب عكسي. وتستنتج TypeScript نوع العدّاد من قيمته الأولى.

صرّح بالعدّاد بـ let لا بـ var. مع let يحصل كل تكرار على ربط خاص به، فترى دالة رد النداء المنشأة داخل الحلقة قيمة ذلك التكرار.

for...of: قيم المصفوفات والنصوص وMaps

يعمل for...of على أي شيء قابل للتكرار، ويأخذ متغير الحلقة النوع المناسب. وللحصول على الفهرس أيضًا، مرّ على array.entries() الذي ينتج tuples من الشكل [index, value].

يخرج break من الحلقة وينتقل continue إلى العنصر التالي، تمامًا كما في حلقة for. تفاصيل وقت التشغيل (الكائنات القابلة للتكرار والمولّدات) موجودة في صفحة JavaScript عن for...of وfor...in.

forEach: بلا break وبلا await

يستدعي forEach دالة لكل عنصر ويعيد undefined. وتأخذ معاملات دالة رد النداء أنواعها من المصفوفة، فلا حاجة إلى تصريح بالأنواع. وهناك أمران لا يستطيع فعلهما:

  • التوقف مبكرًا. break داخل دالة رد النداء هو خطأ الترجمة TS1107، Jump target cannot cross function boundary. وreturn ينهي الاستدعاء الحالي فقط، فيتصرف مثل continue.
  • انتظار العمل غير المتزامن. يتجاهل forEach الوعد الذي تعيده دالة رد نداء async.

من السهل ألا تنتبه لفخ العمل غير المتزامن، لأنه يُترجم دون أي تحذير:

تنتهي دوال رد النداء في forEach بترتيب المؤقتات (10 ثم 20 ثم 30) بعد أن تكون الحلقة قد تجاوزتها. أما نسخة for...of فتشغّلها بالتتابع. وللعمل المتوازي الذي تريد انتظاره مع ذلك، استخدم await Promise.all(items.map(async (item) => { ... })).

for...in: المفاتيح نصوص

يمر for...in على أسماء الخصائص القابلة للتعداد في كائن. ونوع المفتاح دائمًا string، حتى في المصفوفات حيث المفاتيح هي الفهارس كنصوص. وهذا يجعله غير مريح للوصول إلى كائن ذي نوع محدد:

index.ts(5,22): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; age: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ name: string; age: number; }'.

تعطي TypeScript المفتاح النوع string عن قصد: وقت التشغيل قد يملك الكائن خصائص أكثر مما يذكره نوعه (التنميط البنيوي يسمح بخصائص إضافية)، ويمر for...in أيضًا على الخصائص الموروثة القابلة للتعداد. لذا لا تستطيع أن تعد بأن المفتاح هو "name" | "age". الحلول في القسم التالي. وتجنّب for...in على المصفوفات تمامًا.

المرور على مفاتيح الكائن وقيمه

يعطي Object.entries أزواج [key, value] وهو الطريقة المعتادة للمرور على كائن. ويعيد Object.keys النوع string[] للسبب نفسه الذي يجعل for...in كذلك، فعندما تعرف أن الكائن ليس فيه مفاتيح إضافية، أكّد نوع المفتاح.

للبيانات التي لا تُعرف مفاتيحها مسبقًا، أعطِ الكائن النوع Record<string, number> أو استخدم Map؛ وكلاهما مشروح في صفحة القاموس.

while وdo...while

يفحص while شرطه قبل كل دورة؛ ويشغّل do...while الجسم مرة واحدة قبل الفحص. وهما مناسبان للحلقات التي لا تمر على مجموعة، مثل القراءة حتى يتحقق شرط. ويعمل التضييق داخلهما: بعد فحص !== undefined في الشرط، يُضيَّق نوع المتغير داخل الجسم.

التسميات (labels) وbreak وcontinue في الحلقات المتداخلة

يخرج break العادي من الحلقة الداخلية فقط. للخروج من حلقة خارجية، ضع لها تسمية واذكر التسمية:

أي حلقة تستخدم

الهدفالحلقة
كل عنصر في مصفوفةfor (const x of arr)
العنصر والفهرسfor (const [i, x] of arr.entries()) أو arr.forEach((x, i) => ...)
التوقف مبكرًاfor...of مع break، أو find وsome وevery
انتظار كل خطوةfor...of مع await بداخلها
بناء مصفوفة جديدةmap وfilter (ليست حلقة)
العدّ أو التقدم بخطوة nfor (let i = 0; ...; i += n)
مفاتيح الكائن وقيمهfor (const [k, v] of Object.entries(obj))
مُدخلات Mapfor (const [k, v] of map)
حتى يتحقق شرطwhile

الأسئلة الشائعة

كيف أكتب حلقة for في TypeScript؟

كما في JavaScript: for (let i = 0; i < 5; i++) { ... }؛ وتستنتج TypeScript أن i من النوع number. وللمرور على عناصر مصفوفة، for (const item of items) أقصر، ويأخذ item نوع عناصر المصفوفة.

كيف أخرج من forEach في TypeScript؟

لا يمكنك: break داخل دالة رد النداء هو خطأ الترجمة TS1107 (Jump target cannot cross function boundary)، وreturn ينهي استدعاء رد النداء الحالي فقط، مثل continue. استخدم for...of مع break، أو some/find/every، التي تتوقف مبكرًا بطبيعتها.

ما الفرق بين for...of وfor...in في TypeScript؟

يمر for...of على قيم كائن قابل للتكرار (عناصر المصفوفة، وأحرف النص، ومُدخلات Map) بأنواعها الحقيقية. ويمر for...in على مفاتيح الخصائص القابلة للتعداد في كائن، ونوعها دائمًا string، بما فيها فهارس المصفوفة كنصوص. استخدم for...of للمصفوفات وObject.entries للكائنات.

كيف أمر على مفاتيح كائن وقيمه في TypeScript؟

استخدم for (const [key, value] of Object.entries(obj)). يكون key من النوع string وvalue هو union أنواع الخصائص. وإذا احتجت إلى أن يكون key من النوع keyof typeof obj، فأكّد النوع: (Object.keys(obj) as (keyof typeof obj)[])، لأن TypeScript تعطي Object.keys النوع string[] عن قصد.

هل يمكن استخدام await داخل forEach؟

يُترجم الكود، لكن forEach لا ينتظر الوعود التي تعيدها دالة رد النداء، فتنتهي الحلقة قبل انتهاء العمل. استخدم for...of مع await بداخلها للعمل المتتابع، أو await Promise.all(items.map(async (x) => ...)) لتشغيله بالتوازي.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن