خصائص الكائنات الديناميكية في TypeScript: شرح للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

كيفية تعيين الخصائص ديناميكيًا لكائن في TypeScript؟ شرح واضح للمبتدئين

\n\n

مرحبًا يا صديقي! إذا كنت قد كتبت يومًا ما بلغة JavaScript ثم انتقلت إلى TypeScript، فبالتأكيد واجهت موقفًا تحتاج فيه إلى إضافة خاصية جديدة لكائن أثناء التنفيذ. في JS يتم ذلك بسهولة: obj.newProp = 'value'. ولكن بمجرد أن تجرب نفس الشيء في TypeScript، سيبدأ المترجم في التذمر: "Property 'newProp' does not exist on type ...". هل هذا مألوف لك؟

\n\n

لا تقلق، هذا طبيعي. TypeScript يحمينا من الأخطاء العشوائية، لكن أحيانًا تبدو صرامته مفرطة. في هذه المقالة، سنشرح كيفية تعيين الخصائص ديناميكيًا للكائنات بشكل صحيح وآمن. ستتعرف على عدة طرق مجربة - من البسيطة إلى الأكثر أناقة. هيا بنا!

\n\n

لماذا يتذمر TypeScript؟

\n\n

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

\n\n
const user = { name: 'آنا', age: 25 };\n
\n\n

يستنتج TypeScript نوعه تلقائيًا: { name: string; age: number }. هذا يعني أن الكائن user يمكنه احتواء هاتين الخاصيتين فقط. محاولة إضافة email ستسبب خطأ في الترجمة:

\n\n
user.email = 'anna@example.com'; // ❌ خطأ!\n
\n\n

لا يعلم TypeScript أن مثل هذه الخاصية قد تظهر، فيمنع الكود. لكن في الحياة الواقعية، غالبًا ما نحتاج إلى إضافة خصائص ديناميكيًا - مثلاً عند العمل مع بيانات من API أو نماذج. دعنا نرى كيف نفعل ذلك بشكل صحيح.

\n\n

الطريقة 1: الأسهل — as any

\n\n

طريقة سريعة ولكنها غير نظيفة هي استخدام as any. إنها تقول حرفيًا لـ TypeScript: "أوقف التحقق من الأنواع لهذا الكائن".

\n\n
const user: any = { name: 'آنا', age: 25 };\nuser.email = 'anna@example.com'; // ✅ يعمل\nuser.isAdmin = true;             // ✅ يعمل\n
\n\n

لماذا هذا سيء؟ لأنك تفقد كل مزايا TypeScript: الإكمال التلقائي، التحقق من الأنواع، الحماية من الأخطاء الإملائية. إذا كتبت بالخطأ user.emial، لن يظهر خطأ - ستعرف عنه فقط أثناء التشغيل. استخدم هذه الطريقة فقط للنمذجة السريعة أو عندما تعرف بالضبط ما تفعله.

\n\n

الطريقة 2: توقيع الفهرس (Index Signature)

\n\n

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

\n\n
interface 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.

\n\n

ملاحظة مهمة: إذا أردت تقييد أنواع القيم، استبدل any بنوع محدد:

\n\n
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>. إنه مثالي للقواميس أو التعيينات.

\n\n
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;\n\nconst roles: UserRoles = {\n  anna: 'admin',\n  ivan: 'user',\n};\                

المدونات

توصيات الكتب