مقدمة: لماذا يعترض TypeScript على خصائصك؟
أنت تكتب كودًا بلغة TypeScript، كل شيء يسير على ما يرام... وفجأة — بام! خط أحمر متموج تحت خاصية في الفئة، ويصدر المترجم عبارة تقشعر لها الأبدان: Property '...' has no initializer and is not definitely assigned in the constructor. هل هذا مألوف؟ إذا كنت قد عملت مع Angular أو React أو حتى مع فئات TypeScript — فالأرجح أنك واجهته.
هذا الخطأ هو واحد من أشهر الأخطاء على Stack Overflow (آلاف المشاهدات، عشرات الإجابات). لكن الخبر السار: إنه ليس خطأً مخيفًا. الخبر السيئ: إذا لم تفهمه، فقد ترتكب أخطاءً تؤدي إلى أخطاء في وقت التشغيل. في هذه المقالة، سنشرح لماذا يحدث خطأ TypeScript هذا، وكيفية إصلاحه، ومتى يكون كل حل مناسبًا.
ماذا يعني خطأ "Property has no initializer"؟
دعنا نترجم من لغة المترجم إلى لغة البشر. TypeScript يقول لك: "مرحبًا، لقد أعلنت عن خاصية في الفئة، لكنك لم تعطها قيمة أولية ولم تقم بتعيينها في المُنشئ. لست متأكدًا من أنه سيكون هناك شيء ذو معنى عندما تصل إلى هذه الخاصية".
قم بتفعيل strict: true في ملف tsconfig.json — وستقوم تلقائيًا بتفعيل العلم strictPropertyInitialization. هذا هو المسؤول عن هذا الفحص. يبدو الكود كالتالي تقريبًا:
class User { name: string; // ❌ خطأ! age: number; // ❌ خطأ!}يلاحظ TypeScript بحق: name و age قد يكونان undefined إذا نسيت تهيئتهما. ونوعهما هو string و number الصارم، بدون undefined. وهنا يأتي التعارض.
الطريقة رقم 1: التهيئة مباشرة في الإعلان
أبسط وأكثر الطرق أمانًا هي إعطاء الخاصية قيمة فورًا. هذا مناسب بشكل خاص للحقول ذات القيم الافتراضية.
class User { name: string = 'ضيف'; age: number = 0; createdAt: Date = new Date();}متى تستخدم: عندما يكون للحقل قيمة افتراضية معقولة. على سبيل المثال، عداد، علامة، تاريخ الإنشاء. هذه هي أفضل ممارسة في TypeScript — أنت تحدد الحالة الأولية بشكل صريح.
الطريقة رقم 2: التهيئة في المُنشئ
الطريقة الكلاسيكية. إذا كانت القيمة يجب أن تأتي من الخارج — قم بتعيينها في المُنشئ. TypeScript يوافق على ذلك.
class User { name: string; age: number;
constructor(name: string, age: number) { this.name = name; this.age = age; }}مهم: سيتحقق المترجم من أنك قمت بتعيين قيمة لجميع الخصائص. إذا نسيت واحدة — سيظهر الخطأ مرة أخرى.
class User { name: string; age: number;
constructor(name: string) { this.name = name; // age لم يتم تعيينه — خطأ! }}هذا تم عن قصد: TypeScript يحميك من undefined العرضي. إما أن تقوم بتهيئة جميع الحقول، أو تسمح صراحةً بأن تكون غير محددة.
الطريقة رقم 3: عامل التوكيد القطعي (!)
أحيانًا تعرف يقينًا أن الخاصية سيتم تهيئتها لاحقًا، ولكن ليس في المُنشئ. على سبيل المثال، من خلال حقن التبعيات في Angular أو من خلال استدعاء دالة غير متزامنة. في هذه الحالة، استخدم definite assignment assertion — علامة التعجب بعد اسم الخاصية.
class UserService { user!: User; // حسنًا، أعدك بأنها ستكون موجودة
constructor() { this.initUser(); }
initUser() { this.user = new User('أليس', 30); }}تنبيه: هذه أداة قوية ولكنها خطيرة. أنت تقول لـ TypeScript: "لا تتحقق، أنا أعرف ما أفعله". إذا نسيت تهيئة الخاصية — ستحصل على undefined في وقت التشغيل. استخدمها فقط عندما تكون متأكدًا بنسبة 100%.
الطريقة رقم 4: جعل الخاصية اختيارية (?)
إذا