esModuleInterop في tsconfig: دليل شامل لـ TypeScript

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

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

ابدأ الدورة

مقدمة: لماذا esModuleInterop ليس مجرد علامة اختيار في tsconfig

\n\n

إذا كنت قد كتبت يومًا ما بلغة TypeScript وقمت باستيراد مكتبة مثل express أو lodash، فمن المحتمل أنك واجهت موقفًا حيث يشتكي المترجم من import * as express from 'express'. أو على العكس، تستخدم بهدوء import express from 'express'، ولكن في كود JavaScript المُنشأ ترى بنية غريبة مثل module.exports.default. ما هذا السحر؟ السحر (أو عدم وجوده) هو العلمة esModuleInterop في ملف tsconfig.json الخاص بك.

\n\n

هذه المعلمة هي واحدة من أكثر الأسئلة شيوعًا على Stack Overflow (آلاف المشاهدات!)، وليس من المستغرب: بدون فهمها، يمكنك بسهولة الوقوع في فخ عدم توافق الوحدات. في هذا البرنامج التعليمي، لن نشرح النظرية فحسب، بل سننتقل من المشكلة إلى الحل مع أمثلة حية. هيا بنا!

\n\n

المشكلة: صراع عالمين من الوحدات

\n\n

TypeScript هي طبقة فوق JavaScript، وتاريخيًا، لدى JavaScript نظامان رئيسيان للوحدات:

\n
    \n
  • CommonJS (CJS) — معيار Node.js، يستخدم require() و module.exports. مثال: const express = require('express');
  • \n
  • ES Modules (ESM) — المعيار الحديث، يستخدم import و export. مثال: import express from 'express';
  • \n
\n

المشكلة هي أن هذه الأنظمة تتعامل بشكل مختلف مع التصدير الافتراضي (default export). في ESM، يمكن أن تحتوي الوحدة على export default واحد، ويتم استيرادها كـ import name from 'module'. في CommonJS، لا يوجد مفهوم "التصدير الافتراضي" — يوجد فقط كائن module.exports الذي يمكن أن يكون أي شيء.

\n\n

عندما يقوم TypeScript بتجميع الكود الخاص بك إلى CommonJS (وهو أمر مطلوب غالبًا لـ Node.js)، يجب عليه أن يقرر: كيف يمثل import x from 'y' كـ require()؟ بدون esModuleInterop، يقوم TypeScript بذلك حرفيًا: ينشئ module.exports.default، مما يعطل استيراد المكتبات المكتوبة تحت CommonJS.

\n\n

ما هو esModuleInterop؟

\n\n

esModuleInterop هي علامة في tsconfig.json تقوم بتمكين توافق إضافي بين CommonJS و ES Modules. عندما يتم تعيينها على true، يقوم TypeScript بإنشاء كود مساعد يسمح لك بكتابة import foo from 'bar' حتى لوحدات CommonJS التي لا تحتوي على تصدير default.

\n\n

افتراضيًا في TypeScript 4.x والإصدارات الأحدث، هذه العلامة تساوي true (إذا كنت تستخدم --init)، ولكن في المشاريع القديمة غالبًا ما كانت معطلة. لماذا؟ لأنها تغير سلوك الاستيراد، وبدون فهم ذلك يؤدي إلى أخطاء.

\n\n

كيف يبدو ذلك في tsconfig.json

\n
{\n  "compilerOptions": {\n    "esModuleInterop": true,\n    // إعدادات أخرى...\n  }\n}\n
\n\n

تمكين هذه العلامة يعمل أيضًا تلقائيًا على تمكين allowSyntheticDefaultImports، الذي يسمح بالاستيراد الافتراضي من الوحدات التي لا تحتوي على تصدير default فعلي على مستوى الأنواع.

\n\n

برنامج تعليمي خطوة بخطوة: من المشكلة إلى الحل

\n\n

دعنا نحلل الموقف بمثال حقيقي. لنفترض أن لدينا مكتبة بسيطة على CommonJS:

\n\n
// myLib.js (CommonJS)\nmodule.exports = function greet(name) {\n  return `Hello, ${name}!`;\n};\n
\n\n

نريد استخدامها في مشروع TypeScript. لننشئ ملف index.ts:

\n\n
// index.ts\nimport greet from './myLib';\nconsole.log(greet('World'));\n
\n\n

الآن دعنا نرى ما سيحدث عند التجميع بإعدادات مختلفة لـ esModuleInterop.

\n\n

الخطوة 1: بدون esModuleInterop (false)

\n\n

لنقم بتشغيل التجميع مع "esModuleInterop": false. سيعطي TypeScript خطأ:

\n
Module '"myLib"' has no d                

المدونات

توصيات الكتب