كيفية تجاوز نوع خاصية الواجهة من ملف .d.ts في TypeScript: دليل كامل

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

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

ابدأ الدورة

مقدمة: لماذا لا يمكنك ببساطة تغيير النوع؟

\n\n

تخيل الموقف التالي: أنت تستخدم مكتبة قوية للعمل مع الرسوم البيانية. لديها توثيق مثالي، ومئات النجوم على GitHub، وحتى ملفات .d.ts مع وصف الأنواع مرفقة. ولكن هناك مشكلة واحدة - قرر مطور المكتبة أن الحقل data يجب أن يكون من النوع string | number، بينما أنت بحاجة ماسة إلى النوع Date. تذهب إلى مجلد node_modules، وتعدل الملف - ثم ينهار كل شيء عند تنفيذ npm install التالي.

\n\n

هل هذا مألوف؟ هذه واحدة من أكثر المشاكل شيوعًا التي يواجهها مطورو TypeScript. سؤال «تجاوز نوع خاصية واجهة معرفة في ملف Typescript d.ts» حصد آلاف المشاهدات على Stack Overflow. وليس من دون سبب - فالشيطان يكمن في التفاصيل.

\n\n

في هذه المقالة، سنستعرض 5 طرق قانونية لتجاوز نوع خاصية واجهة من ملف .d.ts خارجي. لا حيل مع نسخ الملفات - فقط TypeScript نقي.

\n\n

فهم المشكلة: لماذا يقاوم TypeScript؟

\n\n

قبل أن نخترق النظام، دعنا نفهم منطقه. TypeScript صارم مع الأنواع ليس من باب الخبث، بل من أجل أمانك. عندما تحاول تجاوز خاصية واجهة، فأنت في الواقع تعلن: «أنا أعرف أفضل من مؤلف المكتبة». ويسألك TypeScript: «هل أنت متأكد؟ أثبت ذلك».

\n\n

لننظر إلى مثال تقليدي. لنفترض أن لدينا واجهة من مكتبة:

\n\n
// node_modules/some-library/types.d.ts\nexport interface UserConfig {\n  id: string | number;\n  name: string;\n  createdAt: Date | string;\n  metadata: Record<string, unknown>;\n}
\n\n

تريد أن يكون createdAt من النوع Date فقط. الوراثة البسيطة لن تنجح - سيعطيك TypeScript خطأ إذا حاولت تضييق النوع. ولكن هناك طرق بديلة.

\n\n

الطريقة 1: Declaration Merging (دمج التصريحات)

\n\n

الطريقة الأكثر أناقة عندما تكون ممكنة. إذا كانت المكتبة تصدر واجهة (interface) وليس نوعًا (type)، يمكنك «تكميلها» في نطاق عام أو في وحدة نمطية.

\n\n

شرط مهم: الواجهات التي تحمل نفس الاسم في نفس النطاق يتم دمجها تلقائيًا. لكن لا يمكن تجاوز خاصية موجودة - فقط إضافة خاصية جديدة.

\n\n

لذلك، هذه الطريقة مناسبة عندما تحتاج إلى توسيع الواجهة، وليس تغيير حقل موجود. على سبيل المثال:

\n\n
// global.d.ts\nexport interface UserConfig {\n  customField: boolean; // ✅ إضافة حقل جديد\n}\n\n// أما هكذا - خطأ:\nexport interface UserConfig {\n  createdAt: Date; // ❌ خطأ: تكرار المعرف\n}
\n\n

الخلاصة: Declaration Merging أداة ممتازة لإضافة الحقول، ولكنها عديمة الفائدة لتجاوز الحقول الموجودة.

\n\n

الطريقة 2: استخدام Omit وواجهة مخصصة

\n\n

هذه هي الرصاصة الفضية لـ 90% من الحالات. الفكرة بسيطة: «نقطع» الخاصية المشكلة باستخدام Omit وننشئ نوعًا جديدًا بالنوع المطلوب.

\n\n
// الكود الخاص بك\ntype MyUserConfig = Omit<UserConfig, 'createdAt'> & {\n  createdAt: Date; // ✅ الآن فقط Date\n};\n\n// الاستخدام\nconst config: MyUserConfig = {\n  id: 42,\n  name: 'Alice',\n  createdAt: new Date('2024-01-01'), // يعمل\n  metadata: { role: 'admin' }\n};
\n\n

ماذا يحدث هنا؟ Omit<UserConfig, 'createdAt'> ينشئ نوعًا بدون حقل createdAt. ثم من خلال تقاطع الأنواع (&) نضيف هذا الحقل مرة أخرى، ولكن بالنوع المطلوب.

\n\n

الإيجابيات: مثالي للتغييرات الدقيقة، لا يؤثر على الواجهة الأصلية.
\nالسلبيات: إذا كان هناك العديد من الخصائص، يصبح الكود ضخمًا.

\n\n

الطريقة 3: التوسيع باستخدام Pick و Partial

\n\n

أحيانًا تحتاج إلى تجاوز ليس خاصية واحدة، بل عدة خصائص. أو قد ترغب في جعل جميع الحقول اختيارية،

المدونات

توصيات الكتب