TypeScript: نظام الوحدات النمطية في TypeScript
آخر تحديث: 2026-08-26
تُعد الوحدات النمطية الوحدات الأساسية لتنظيم الكود — يدعم TypeScript الوحدات النمطية ES ووحدات CommonJS، ويضيف إليها إمكانية تصدير الأنواع.
1. أساسيات وحدات ES
(1) تصدير الأسماء
TYPESCRIPT
// utils.ts —— Name Export
export function add(a: number, b: number): number {
return a + b;
}
export function multiply(a: number, b: number): number {
return a * b;
}
export const PI = 3.14159;
TYPESCRIPT
// main.ts —— Import by Name
import { add, multiply, PI } from "./utils";
console.log(add(1, 2)); // 3
console.log(multiply(3, 4)); // 12
console.log(PI); // 3.14159
(2) التصدير الافتراضي
TYPESCRIPT
// logger.ts —— Default Export
export default class Logger {
constructor(private prefix: string) {}
log(message: string): void {
console.log(`[${this.prefix}] ${message}`);
}
}
TYPESCRIPT
// main.ts —— Default Import(No curly braces needed,Custom Name)
import Logger from "./logger";
// That's fine, too.:import MyLogger from "./logger";
let logger = new Logger("APP");
logger.log("App Launch"); // [APP] App Launch
(3) استخدام كل من الصادرات الافتراضية والصادرات المسماة
TYPESCRIPT
// api.ts
export default class ApiClient {
constructor(private baseUrl: string) {}
async get(path: string): Promise<any> {
// ...
}
}
export enum HttpMethod {
GET = "GET",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE"
}
TYPESCRIPT
// main.ts
import ApiClient, { HttpMethod } from "./api";
let client = new ApiClient("https://api.example.com");
let method: HttpMethod = HttpMethod.GET;
▶ مثال: آلة حاسبة معيارية
TYPESCRIPT
// calculator/operations.ts
export function add(a: number, b: number): number { return a + b; }
export function subtract(a: number, b: number): number { return a - b; }
export function multiply(a: number, b: number): number { return a * b; }
export function divide(a: number, b: number): number {
if (b === 0) throw new Error("The divisor cannot be zero.");
return a / b;
}
الناتج:
TEXT
📖 للعرض فقط
// Executed successfully
TYPESCRIPT
// calculator/index.ts
export { add, subtract, multiply, divide } from "./operations";
export type { Operation } from "./types";
// Default Export——Calculator Category
import * as ops from "./operations";
export default class Calculator {
compute(op: string, a: number, b: number): number {
switch (op) {
case "+": return ops.add(a, b);
case "-": return ops.subtract(a, b);
case "*": return ops.multiply(a, b);
case "/": return ops.divide(a, b);
default: throw new Error(`Unknown Operation:${op}`);
}
}
}
2. نوع التصدير
يتيح لك TypeScript تصدير الأنواع بشكل فردي — وهي ميزة يفتقر إليها نظام الوحدات النمطية في JavaScript:
(1) المُعدِّل type
TYPESCRIPT
// types.ts
export interface User {
id: number;
name: string;
email: string;
}
export type UserId = number;
export type UserRole = "admin" | "editor" | "viewer";
// Use export type to explicitly mark"Export Types Only"
export type { User as UserType };
(2) التمييز بين الأنواع والقيم عند الاستيراد
TYPESCRIPT
// main.ts
import { type User, type UserRole, createUser } from "./types";
// ↑ type Modifier Notation"Import Types Only"——It will be erased after compilation.
// Equivalent Old Syntax
// import { User, UserRole } from "./types"; // May result in runtime imports
// Recommended New Syntax——Clearly Distinguish Between Type Import and Value Import
import type { User, UserRole } from "./types";
import { createUser } from "./types";
💡 لماذا يتم هذا التمييز؟
import type يتم مسح الأنواع المستوردة تمامًا بعد التحويل البرمجي — فهي لا تؤدي إلى استدعاءات في وقت التشغيل لـ require أو import. وهذا أمر بالغ الأهمية في الحالات التي تستخدم الأنواع فقط (مثل تعليقات الأنواع والواجهات)، حيث يمنع تحميل الوحدات النمطية غير الضرورية.
(3) استيراد الأنواع المضمنة
TYPESCRIPT
// Mixed Import——Values and Types
import { createUser, type User, type UserRole } from "./types";
// createUser is the value——Requirements for runtime
// User and UserRole is a type——Compile-Time Erasure
3. إعادة التصدير وملفات المجموعات
(1) إعادة التصدير
TYPESCRIPT
// Re-exporting members of one module from another module
export { User, UserId } from "./user-types";
export { Product, ProductId } from "./product-types";
export { Order, OrderId } from "./order-types";
(2) ملف الأسطوانة
index.ts كنقطة دخول للدليل، أعد تصدير جميع واجهات برمجة التطبيقات العامة:
TYPESCRIPT
// models/index.ts —— Bucket files
export { User, UserId } from "./user";
export { Product, ProductId } from "./product";
export { Order, OrderId } from "./order";
export type { CreateUser, UpdateUser } from "./user";
export type { CreateProduct, UpdateProduct } from "./product";
TYPESCRIPT
// Import directly from the directory when using it
import { User, Product, type CreateUser } from "./models";
// without needing to know which specific file it is in
💡 المزايا: يبسط مسارات الاستيراد، ويخفي البنية الداخلية للملف، ويتحكم في واجهة برمجة التطبيقات العامة. العيوب: قد يستورد وحدات غير ضرورية (قد لا تؤدي عملية «tree-shaking» إلى تحسين الكود بشكل كامل).
4. استراتيجيات تحليل الوحدات النمطية
يحتاج TypeScript إلى معرفة كيفية تحويل import "./utils" إلى ملف فعلي — ويتم تحديد ذلك من خلال استراتيجية تحليل الوحدات النمطية.
(1) استراتيجيتان للتحليل النحوي
| الاستراتيجية | الغرض | الوصف |
|---|---|---|
classic |
التوافق مع الإصدارات القديمة | ابحث عن .ts أولاً، ثم .d.ts |
node (موصى به) |
مشروع TS حديث | يحاكي منطق تحليل Node.js |
(2) ترتيب البحث في استراتيجيات تحليل العقد
TEXT
📖 للعرض فقط
import { X } from "./utils"
ترتيب البحث:
./utils.ts./utils.tsx./utils.d.ts- الحقل
typesفي./utils/package.json ./utils/index.ts./utils/index.d.ts
(3) البحث في node_modules
TEXT
📖 للعرض فقط
import _ from "lodash"
ترتيب البحث:
./node_modules/lodash.ts(غير موجود)- حقل
./node_modules/lodash/package.json→types/typings ./node_modules/lodash/index.d.ts./node_modules/@types/lodash/index.d.ts- ابحث عن
../node_modules/→../../node_modules/...
5. تعيين المسارات (Path Mapping)
استخدم الأسماء المستعارة للمسارات في المشاريع الكبيرة لتجنب المسارات النسبية الطويلة:
(1) ملف tsconfig.json للتكوين
JSON
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@utils/*": ["src/utils/*"],
"@models/*": ["src/models/*"],
"@components/*": ["src/components/*"]
}
}
}
(2) استخدام الأسماء المستعارة للمسارات
TYPESCRIPT
// No aliases——Relative paths are prone to errors
import { User } from "../../../models/user";
import { formatDate } from "../../utils/date";
// Has an alias——Clear and concise
import { User } from "@models/user";
import { formatDate } from "@utils/date";
⚠️ ملاحظة: الأسماء المستعارة للمسارات هي مجرد تعيينات في وقت التحويل البرمجي فقط — ولا يتعرف وقت التشغيل (Node.js/المتصفح) على مسارات مثل
@models. تحتاج إلى استخدام أداة بناء (مثل resolve.alias في Webpack، أو resolve.alias في Vite، أو tsc-alias) لإجراء استبدال المسار في وقت التشغيل.
6. قابلية التشغيل البيني مع CommonJS
(1) وحدات CommonJS
TYPESCRIPT
// Use CommonJS style export
// math.cjs
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
module.exports = { add, multiply };
(2) استيراد CommonJS في TS
TYPESCRIPT
// esModuleInterop: false(Default)
import * as math from "./math.cjs";
math.add(1, 2);
// esModuleInterop: true(Recommendations)
import math from "./math.cjs"; // ✅ A More Natural Way to Introduce It
math.add(1, 2);
(3) السماح بتصدير CommonJS من TS
TYPESCRIPT
// Use export = syntax to export CommonJS Style
class Calculator {
add(a: number, b: number): number { return a + b; }
}
export = Calculator;
// Use the following when importing: import = require
import Calculator = require("./calculator");
let calc = new Calculator();
💡 توصية: استخدم وحدات ES (
import/export) في جميع المشاريع الجديدة، وقم بتمكين esModuleInterop: true للحفاظ على التوافق مع حزم CommonJS القديمة. لا تستخدم export = وimport = إلا عندما يكون التوافق التام مع CommonJS ضروريًا.
▶ مثال: التصدير المُسمى مقابل التصدير الافتراضي جنبًا إلى جنب
TYPESCRIPT
// config.ts — named and default exports together
export const APP_NAME = "MyApp";
export const VERSION = "1.0.0";
export default class AppConfig {
constructor(public port: number = 3000) {}
toString(): string { return `${APP_NAME} v${VERSION} on :${this.port}`; }
}
TYPESCRIPT
// main.ts — importing both styles
import AppConfig, { APP_NAME, VERSION } from "./config";
let config = new AppConfig(8080);
console.log(config.toString()); // MyApp v1.0.0 on :8080
console.log(APP_NAME); // MyApp
الناتج:
TEXT
📖 للعرض فقط
MyApp v1.0.0 on :8080
MyApp
▶ مثال: import type للاستيرادات الخاصة بالنوع فقط
TYPESCRIPT
// shapes.ts
export interface Circle { kind: "circle"; radius: number; }
export interface Square { kind: "square"; side: number; }
export type Shape = Circle | Square;
export function area(shape: Shape): number {
return shape.kind === "circle"
? Math.PI * shape.radius ** 2
: shape.side ** 2;
}
TYPESCRIPT
// main.ts — import type for types, regular import for values
import type { Circle, Square, Shape } from "./shapes";
import { area } from "./shapes";
let c: Circle = { kind: "circle", radius: 5 };
let s: Shape = { kind: "square", side: 4 };
console.log(area(c)); // 78.5398...
console.log(area(s)); // 16
الناتج:
TEXT
📖 للعرض فقط
78.53981633974483
16
❓ أسئلة شائعة
س ما الفرق بين
import type وimport العادي؟ج
import type يتم مسح الأنواع المستوردة بهذه الطريقة تمامًا بعد التحويل البرمجي، ولا تؤدي إلى تحميل الوحدات النمطية أثناء وقت التشغيل. أما عبارة import العادية فتستورد القيم والأنواع على حد سواء، وتؤدي إلى استدعاء require() بعد التحويل البرمجي. عند استخدام الأنواع فقط (مثل الواجهات أو الأسماء المستعارة للأنواع)، تأكد من استخدام import type لتجنب عمليات الاستيراد غير الضرورية في وقت التشغيل.س هل ينبغي استخدام ملفات «البوكيت» (index.ts)؟
ج يُنصح باستخدامها في المكتبات وواجهات برمجة التطبيقات (API) العامة — لتبسيط عمليات الاستيراد والتحكم في الواجهات المصدرة. أما بالنسبة للتطبيقات الداخلية، فإن الأمر يعتمد على الحالة — فهي ليست ضرورية للمشاريع الصغيرة، لكنها توفر مزايا تنظيمية للمشاريع الكبيرة. العيب الرئيسي هو أنها قد تؤثر على عملية «تري-شيكينغ»، لكن أدوات التجميع الحديثة تتعامل مع هذا الأمر بشكل جيد.
س كيف يتم تطبيق الأسماء المستعارة للمسارات أثناء وقت التشغيل؟
ج لا يزال كود JS الذي يُنشئه TypeScript يستخدم مسارات الأسماء المستعارة (مثل
@models/user)، والتي لا يتم التعرف عليها أثناء وقت التشغيل. يجب أن تتم معالجة استبدال المسارات بواسطة أداة بناء — يستخدم Webpack resolve.alias، ويستخدم Vite resolve.alias، بينما يتطلب التجميع النقي بواسطة tsc معالجة لاحقة بواسطة أدوات مثل tsc-alias.س أيهما يجب أن أستخدم، ES Modules أم CommonJS؟
ج بالنسبة للمشاريع الجديدة، استخدم ES Modules حصريًّا (
import/export). CommonJS هو نظام الوحدات النمطية القديم في Node.js ويجري التخلي عنه تدريجيًّا. يتيح لك الخيار esModuleInterop في TypeScript استخدام حزم CommonJS بسلاسة. ولا تدعم المتصفحات وDeno سوى ES Modules.📖 ملخص
- تستخدم وحدات ES
export/importكرموز تنظيمية؛ وتُستخدم الأقواس المتعرجة للمخرجات المسماة، ولكن لا تُستخدم للمخرجات الافتراضية. import typeأنواع الاستيراد فقط — يتم مسحها بعد الترجمة؛ ولا يتم تحميل أي وحدات نمطية أثناء التشغيل- يقوم ملف
index.tsبإعادة تصدير واجهات برمجة التطبيقات العامة الموجودة في الدليل لتبسيط مسار الاستيراد - استراتيجية تحليل الوحدات النمطية
node(موصى بها) تحاكي منطق البحث في Node.js - تعيين المسارات (
baseUrl+paths) يستبدل المسارات النسبية الطويلة بأسماء مستعارة؛ ويتطلب أداة بناء تدعم هذه الميزة esModuleInterop: trueتمكين التوافق السلس بين وحدات ES و CommonJS
📝 تمارين
- تمرين أساسي (مستوى الصعوبة ⭐): أنشئ ثلاثة ملفات وحدات —
math.ts(تقوم بتصدير عمليات الجمع والطرح)، وstring-utils.ts(تقوم بتصدير عمليات تحويل الأحرف إلى كبيرة وعكسها)، وindex.ts(تقوم بإعادة تصدير ملف «bucket»). قم باستيرادها واستخدامها فيmain.ts. - تمرين متقدم (درجة الصعوبة ⭐⭐): قم بتكوين تعيينات المسارات لمشروع موجود — قم بتعيين
@utilsإلىsrc/utils، و@modelsإلىsrc/models. استخدمimport typeلاستيراد الأنواع، واستخدمimport(بدون تعيين) لاستيراد القيم. - التحدي (الصعوبة: ⭐⭐⭐): اكتب ملف إعلان يتيح استيراد حزمة CommonJS
legacy-sdkإلى TypeScript بنمط ES Module —import LegacySDK from "legacy-sdk". ضع في اعتبارك كلتا الحالتين: عند تمكينesModuleInteropوعند تعطيله.