كيفية تعيين الخصائص ديناميكيًا لكائن في TypeScript؟ شرح واضح للمبتدئين
\n\nمرحبًا يا صديقي! إذا كنت قد كتبت يومًا ما بلغة JavaScript ثم انتقلت إلى TypeScript، فبالتأكيد واجهت موقفًا تحتاج فيه إلى إضافة خاصية جديدة لكائن أثناء التنفيذ. في JS يتم ذلك بسهولة: obj.newProp = 'value'. ولكن بمجرد أن تجرب نفس الشيء في TypeScript، سيبدأ المترجم في التذمر: "Property 'newProp' does not exist on type ...". هل هذا مألوف لك؟
لا تقلق، هذا طبيعي. TypeScript يحمينا من الأخطاء العشوائية، لكن أحيانًا تبدو صرامته مفرطة. في هذه المقالة، سنشرح كيفية تعيين الخصائص ديناميكيًا للكائنات بشكل صحيح وآمن. ستتعرف على عدة طرق مجربة - من البسيطة إلى الأكثر أناقة. هيا بنا!
\n\nلماذا يتذمر TypeScript؟
\n\nقبل حل المشكلة، دعنا نفهم جذرها. TypeScript هي طبقة فوق JavaScript تضيف كتابة الأنواع الثابتة. عندما تعلن عن كائن:
\n\nconst user = { name: 'آنا', age: 25 };\n\n\nيستنتج TypeScript نوعه تلقائيًا: { name: string; age: number }. هذا يعني أن الكائن user يمكنه احتواء هاتين الخاصيتين فقط. محاولة إضافة email ستسبب خطأ في الترجمة:
user.email = 'anna@example.com'; // ❌ خطأ!\n\n\nلا يعلم TypeScript أن مثل هذه الخاصية قد تظهر، فيمنع الكود. لكن في الحياة الواقعية، غالبًا ما نحتاج إلى إضافة خصائص ديناميكيًا - مثلاً عند العمل مع بيانات من API أو نماذج. دعنا نرى كيف نفعل ذلك بشكل صحيح.
\n\nالطريقة 1: الأسهل — as any
\n\nطريقة سريعة ولكنها غير نظيفة هي استخدام as any. إنها تقول حرفيًا لـ TypeScript: "أوقف التحقق من الأنواع لهذا الكائن".
const user: any = { name: 'آنا', age: 25 };\nuser.email = 'anna@example.com'; // ✅ يعمل\nuser.isAdmin = true; // ✅ يعمل\n\n\nلماذا هذا سيء؟ لأنك تفقد كل مزايا TypeScript: الإكمال التلقائي، التحقق من الأنواع، الحماية من الأخطاء الإملائية. إذا كتبت بالخطأ user.emial، لن يظهر خطأ - ستعرف عنه فقط أثناء التشغيل. استخدم هذه الطريقة فقط للنمذجة السريعة أو عندما تعرف بالضبط ما تفعله.
الطريقة 2: توقيع الفهرس (Index Signature)
\n\nنهج أكثر صحة هو الإعلان مسبقًا أن الكائن يمكن أن يحتوي على خصائص ديناميكية. لهذا نستخدم توقيع الفهرس.
\n\ninterface User {\n name: string;\n age: number;\n [key: string]: any; // 👈 نسمح بأي خصائص إضافية\n}\n\nconst user: User = { name: 'آنا', age: 25 };\nuser.email = 'anna@example.com'; // ✅ يعمل\nuser.phone = '+7-999-123-45-67'; // ✅ يعمل\n\n\nماذا يحدث هنا؟ العبارة [key: string]: any تعني: "يمكن أن يحتوي الكائن على أي مفاتيح نصية، ويمكن أن تكون قيمها من أي نوع". الآن يمكنك إضافة خصائص ديناميكيًا، ولن يتذمر TypeScript.
ملاحظة مهمة: إذا أردت تقييد أنواع القيم، استبدل any بنوع محدد:
interface User {\n name: string;\n age: number;\n [key: string]: string | number; // فقط نصوص وأرقام\n}\n\n\nتوقيع الفهرس هو اختيار ممتاز عندما تعرف مسبقًا أن الكائن سيتوسع. لكن كن حذرًا: إذا كانت هناك خصائص أخرى في الواجهة بأنواع مختلفة، فيجب أن تتوافق أيضًا مع التوقيع.
\n\nالطريقة 3: النوع المساعد Record
\n\nإذا كنت بحاجة إلى إنشاء كائن حيث تكون جميع المفاتيح والقيم من نوع واحد، استخدم النوع المدمج Record<Keys, Type>. إنه مثالي للقواميس أو التعيينات.
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;\n\nconst roles: UserRoles = {\n anna: 'admin',\n ivan: 'user',\n};\