مترجم TypeScript والتكوين
فهم مترجم TypeScript
مترجم TypeScript (tsc) هو الأداة التي تحول كود TypeScript الخاص بك إلى JavaScript. فهم كيفية تكوين والتحكم في المترجم أمر أساسي لبناء تطبيقات TypeScript قوية. المترجم يفعل أكثر بكثير من مجرد إزالة تعليقات النوع - يمكنه فحص الأخطاء، وتحويل بناء الجملة الحديث، وتحسين الإخراج، وفرض معايير البرمجة.
ملف tsconfig.json
ملف tsconfig.json هو قلب أي مشروع TypeScript. إنه يخبر المترجم بكيفية معالجة الكود الخاص بك وما القواعد التي يجب فرضها. عندما تقوم بتشغيل tsc بدون أي وسائط في دليل يحتوي على ملف tsconfig.json، يستخدم المترجم الإعدادات من هذا الملف.
إنشاء tsconfig.json
أسهل طريقة لإنشاء ملف tsconfig.json هي استخدام علامة --init:
tsc --init
ينشئ هذا ملف tsconfig.json مع العديد من الخيارات المُعلَّقة. لنبدأ بتكوين أدنى ونبني من هناك:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
مهم: يستخدم ملف tsconfig.json تنسيق JSON، مما يعني عدم وجود فواصل لاحقة ويجب أن تكون جميع المفاتيح بين علامات اقتباس مزدوجة. التعليقات مدعومة باستخدام بناء الجملة // أو /* */، على الرغم من أن JSON القياسي لا يسمح بالتعليقات.
خيارات المترجم الأساسية
الهدف والوحدة (Target and Module)
يتحكم هذان الخياران في إصدار JavaScript الذي يُخرجه المترجم:
target
يحدد إصدار ECMAScript الذي سيتم التجميع إليه. القيم الشائعة:
"target": "ES5" // للمتصفحات القديمة (IE11) "target": "ES6" // يُعرف أيضاً باسم ES2015 "target": "ES2020" // JavaScript الحديث "target": "ESNext" // أحدث الميزات
التوصية: استخدم "ES2020" أو "ES2021" للتطبيقات الحديثة. استخدم "ES5" فقط إذا كنت بحاجة إلى دعم متصفحات قديمة جداً.
module
يحدد نظام الوحدة للإخراج:
"module": "commonjs" // نمط Node.js (require/module.exports) "module": "es2015" // وحدات ES الحديثة (import/export) "module": "esnext" // أحدث ميزات الوحدة "module": "amd" // لمحمّلات وحدات المتصفح "module": "umd" // تعريف الوحدة العالمي
الإعداد الشائع: لمشاريع Node.js، استخدم "commonjs". لمشاريع الواجهة الأمامية الحديثة مع أدوات التجميع مثل Webpack أو Vite، استخدم "esnext" أو "es2015".
التحكم في الإخراج
outDir و rootDir
التحكم في مكان وضع الملفات المُجمَّعة:
{
"compilerOptions": {
"rootDir": "./src", // أين توجد ملفات TypeScript
"outDir": "./dist" // أين يذهب JavaScript المُجمَّع
}
}
مع هذا التكوين، يتم تجميع ملف في src/utils/helper.ts إلى dist/utils/helper.js، مع الحفاظ على بنية الدليل.
خيارات الإخراج الأخرى
{
"compilerOptions": {
"removeComments": true, // إزالة التعليقات من الإخراج
"sourceMap": true, // توليد ملفات .map للتصحيح
"declaration": true, // توليد ملفات تعريف النوع .d.ts
"declarationMap": true, // توليد خرائط المصدر لملفات .d.ts
"inlineSourceMap": false, // تضمين خرائط المصدر مضمّنة
"outFile": "./dist/bundle.js" // دمج جميع الإخراج في ملف واحد
}
}
تحذير: يعمل خيار outFile فقط مع "module": "amd" أو "module": "system". لمعظم المشاريع الحديثة، استخدم أداة تجميع مثل Webpack بدلاً من outFile.
دقة الوحدة (Module Resolution)
{
"compilerOptions": {
"moduleResolution": "node", // استخدام دقة وحدة Node.js
"baseUrl": "./", // الدليل الأساسي لدقة الوحدة
"paths": { // تعيين المسار للواردات
"@utils/*": ["src/utils/*"],
"@models/*": ["src/models/*"],
"@components/*": ["src/components/*"]
},
"esModuleInterop": true, // تمكين التوافق بين CommonJS و ES modules
"allowSyntheticDefaultImports": true, // السماح بالواردات الافتراضية من الوحدات بدون تصدير افتراضي
"resolveJsonModule": true // السماح باستيراد ملفات .json
}
}
مع تكوين تعيين المسار، يمكنك الاستيراد مثل هذا:
// بدلاً من الواردات النسبية
import { formatDate } from '../../../utils/dateFormatter';
// استخدم أسماء مستعارة نظيفة للمسار
import { formatDate } from '@utils/dateFormatter';
ملاحظة: تعيين المسار في tsconfig.json هو فقط لتجميع TypeScript. إذا كنت تستخدم أداة تجميع، فستحتاج إلى تكوين نفس المسارات هناك (على سبيل المثال، في resolve.alias في Webpack).
خيارات الوضع الصارم (Strict Mode)
يمكّن الوضع الصارم في TypeScript جميع خيارات فحص النوع الصارمة. يُوصى بشدة بهذا للمشاريع الجديدة:
{
"compilerOptions": {
"strict": true
}
}
تعيين "strict": true يمكّن جميع هذه الخيارات:
خيارات صارمة فردية
strictNullChecks
عند التمكين، لا يكون null و undefined في نوع كل قيمة:
// بدون strictNullChecks let name: string = null; // OK // مع strictNullChecks let name: string = null; // Error: Type 'null' is not assignable to type 'string' let name: string | null = null; // OK - السماح صراحةً بـ null
strictFunctionTypes
يمكّن فحصاً أكثر صرامة لأنواع معلمات الدالة:
type Logger = (message: string | number) => void;
// بدون strictFunctionTypes - OK
const numberLogger: Logger = (message: number) => {
console.log(message);
};
// مع strictFunctionTypes - Error
// يجب أن تكون معلمات الدالة متوافقة
strictBindCallApply
يتحقق من أن أساليب bind و call و apply يتم استدعاؤها بوسائط صحيحة:
function greet(name: string, age: number) {
console.log(`Hello ${name}, you are ${age} years old`);
}
// مع strictBindCallApply
greet.call(null, "Alice", 25); // OK
greet.call(null, "Alice", "25"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
greet.apply(null, ["Alice"]); // Error: Expected 2 arguments, but got 1
noImplicitAny
يُثير خطأً عندما لا يستطيع TypeScript استنتاج نوع ويعود افتراضياً إلى any:
// بدون noImplicitAny - OK
function log(message) { // message هو ضمنياً 'any'
console.log(message);
}
// مع noImplicitAny - Error
function log(message) { // Error: Parameter 'message' implicitly has an 'any' type
console.log(message);
}
// تم الإصلاح
function log(message: string) { // OK
console.log(message);
}
noImplicitThis
يُثير خطأً عندما يكون لـ this نوع any ضمني:
// بدون noImplicitThis
const obj = {
name: "Alice",
greet() {
return function() {
console.log(this.name); // 'this' هو ضمنياً 'any'
};
}
};
// مع noImplicitThis - يجب تحديد نوع this
const obj = {
name: "Alice",
greet() {
return function(this: { name: string }) {
console.log(this.name);
};
}
};
alwaysStrict
يضمن تحليل جميع الملفات في وضع ECMAScript الصارم وينبعث "use strict" في كل ملف إخراج:
// سيتضمن JavaScript الناتج: "use strict"; // هذا يمنع مخاطر JavaScript الشائعة مثل: x = 10; // Error: Cannot assign to undeclared variable
أفضل ممارسة: قم دائماً بتمكين الوضع الصارم للمشاريع الجديدة. إنه يلتقط العديد من الأخطاء المحتملة ويفرض ممارسات برمجة أفضل. بالنسبة لمشاريع JavaScript الحالية التي يتم ترحيلها إلى TypeScript، قد تبدأ بتعطيل الوضع الصارم وتمكين خيارات صارمة تدريجياً واحداً تلو الآخر.
خيارات مهمة أخرى
خيارات جودة الكود
{
"compilerOptions": {
"noUnusedLocals": true, // خطأ على المتغيرات المحلية غير المستخدمة
"noUnusedParameters": true, // خطأ على معلمات الدالة غير المستخدمة
"noImplicitReturns": true, // خطأ عندما لا تُرجع جميع مسارات الكود قيمة
"noFallthroughCasesInSwitch": true, // خطأ في حالات السقوط في switch
"allowUnreachableCode": false, // خطأ في الكود غير القابل للوصول
"allowUnusedLabels": false // خطأ في التسميات غير المستخدمة
}
}
خيارات المكتبة وتعريف النوع
{
"compilerOptions": {
"lib": ["ES2020", "DOM"], // تعريفات النوع المراد تضمينها
"types": ["node", "jest"], // حزم @types المحددة المراد تضمينها
"skipLibCheck": true // تخطي فحص النوع لملفات التصريح
}
}
خيار lib حاسم - إنه يخبر TypeScript بواجهات برمجة التطبيقات المدمجة المتاحة:
// لمشاريع Node.js "lib": ["ES2020"] // لمشاريع المتصفح "lib": ["ES2020", "DOM", "DOM.Iterable"] // للمشاريع التي تحتاج إلى كليهما "lib": ["ES2020", "DOM", "WebWorker"]
خيارات تجريبية ومتقدمة
{
"compilerOptions": {
"experimentalDecorators": true, // تمكين بناء جملة المزخرف التجريبي
"emitDecoratorMetadata": true, // إصدار البيانات الوصفية للمزخرفات
"forceConsistentCasingInFileNames": true, // ضمان أحرف اسم الملف المتسقة
"isolatedModules": true, // تأكد من أنه يمكن نقل كل ملف بأمان
"jsx": "react", // وضع JSX لـ React
"incremental": true, // تمكين التجميع المتزايد للبناء الأسرع
"tsBuildInfoFile": "./dist/.tsbuildinfo" // أين يتم تخزين معلومات البناء المتزايد
}
}
التضمين والاستبعاد (Include and Exclude)
التحكم في الملفات التي يعالجها المترجم:
{
"include": [
"src/**/*" // تضمين جميع الملفات في src والأدلة الفرعية
],
"exclude": [
"node_modules", // استبعاد التبعيات
"**/*.spec.ts", // استبعاد ملفات الاختبار
"**/*.test.ts", // استبعاد ملفات الاختبار
"dist" // استبعاد الإخراج المُجمَّع
]
}
السلوك الافتراضي: إذا لم تحدد include، فإن TypeScript يتضمن جميع ملفات .ts و .tsx و .d.ts في المشروع. إذا لم تحدد exclude، فإنه يستبعد تلقائياً node_modules و bower_components ودليل الإخراج.
مثال واقعي كامل
هنا tsconfig.json شامل لمشروع Node.js حديث:
{
"compilerOptions": {
// اللغة والبيئة
"target": "ES2020",
"lib": ["ES2020"],
"module": "commonjs",
"moduleResolution": "node",
// الإخراج
"rootDir": "./src",
"outDir": "./dist",
"removeComments": true,
"sourceMap": true,
"declaration": true,
// فحص النوع
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
// دقة الوحدة
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
// الأداء
"skipLibCheck": true,
"incremental": true,
// تعيين المسار
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
"@utils/*": ["src/utils/*"],
"@models/*": ["src/models/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
}
استخدام سطر أوامر المترجم (Compiler CLI)
الأوامر الأساسية
// التجميع مع tsconfig.json tsc // تجميع ملف محدد tsc file.ts // التجميع مع تكوين مخصص tsc --project tsconfig.production.json // وضع المراقبة tsc --watch // عرض إصدار المترجم tsc --version // عرض المساعدة tsc --help
تجاوز خيارات التكوين
يمكنك تجاوز خيارات tsconfig.json من سطر الأوامر:
// تجاوز target tsc --target ES5 // تجاوز outDir tsc --outDir ./build // تمكين خرائط المصدر tsc --sourceMap true // التجميع على الرغم من الأخطاء tsc --noEmitOnError false
ملفات تكوين متعددة
للمشاريع المعقدة، قد يكون لديك تكوينات مختلفة لبيئات مختلفة:
// tsconfig.json (التكوين الأساسي)
{
"compilerOptions": {
"strict": true,
"target": "ES2020"
}
}
// tsconfig.dev.json (التطوير)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"incremental": true
}
}
// tsconfig.prod.json (الإنتاج)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"removeComments": true,
"sourceMap": false
}
}
استخدمها مع:
tsc --project tsconfig.dev.json tsc --project tsconfig.prod.json
تمرين عملي
أنشئ مشروع TypeScript مع تكوين مناسب:
- أنشئ دليلاً جديداً يسمى
typescript-config-practice - تهيئة npm:
npm init -y - تثبيت TypeScript:
npm install --save-dev typescript - أنشئ
tsconfig.jsonمع:- تمكين الوضع الصارم
- هدف ES2020
- تمكين خرائط المصدر
- src/ كدليل جذر
- dist/ كدليل إخراج
- أنشئ
src/index.tsمع:interface User { id: number; name: string; email: string; } function getUser(id: number): User { return { id, name: "John Doe", email: "john@example.com" }; } const user = getUser(1); console.log(`User: ${user.name} (${user.email})`); - التجميع باستخدام
npx tsc - تشغيل الكود المُجمَّع:
node dist/index.js - حاول إزالة خاصية البريد الإلكتروني من الكائن المُرجع واطلع على الخطأ الذي يعطيه TypeScript
- قم بتمكين وضع المراقبة وقم بإجراء تغييرات لرؤية إعادة التجميع التلقائي
الملخص
- يتحكم ملف
tsconfig.jsonفي كيفية تجميع TypeScript للكود الخاص بك - يتحكم
targetوmoduleفي إصدار JavaScript ونظام الوحدة - يتحكم
outDirوrootDirفي تنظيم الملفات "strict": trueيمكّن جميع خيارات فحص النوع الصارمة ويُوصى به بشدة- يسمح تعيين المسار بواردات نظيفة بدون مسارات نسبية معقدة
- يمكن للمترجم توليد خرائط المصدر وملفات التصريح وتحسين الإخراج
- يمكن أن يكون لديك ملفات تكوين متعددة لبيئات مختلفة باستخدام
extends - يوفر سطر أوامر مترجم TypeScript العديد من الخيارات للتجميع وفحص الأخطاء
الخطوات التالية: الآن بعد أن فهمت كيفية تكوين TypeScript، سنستكشف نظام النوع بالتفصيل، بدءاً من الأنواع الأساسية في الدرس التالي.