TypeScript: الأنواع المتقاطعة في TypeScript والتركيب المتقدم

آخر تحديث: 2026-08-26

تستخدم أنواع التقاطع & لدمج أنواع متعددة في نوع واحد — حيث يمتلك النوع الجديد جميع خصائص كل نوع من الأنواع الأصلية. وهي أداة أساسية للأنواع المركبة في TypeScript.

1. أساسيات أنواع التقاطعات

(1) قواعد النحو الأساسية

TYPESCRIPT
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;

let person: Person = {
  name: "Charlie",
  age: 20
  // Both must be present name and age
};

(2) المقارنة مع أنواع الاتحاد

المشغل المعنى التشبيه
A & B (أو) يستوفي كل من A و B و (و)
`A B` (حرف ربط) يستوفي الشرط A أو B
TYPESCRIPT
type StringOrNumber = string | number;   // Joint:It could be one of them
type StringAndNumber = string & number;   // Crossing:It must be both at the same time → never!

// Meaningful Crossovers——Object Type
type HasId = { id: number };
type HasName = { name: string };
type Entity = HasId & HasName;   // ✅ At the same time, there are id and name

(3) التداخل بين أنواع متعددة

TYPESCRIPT
type Timestamped = { createdAt: Date; updatedAt: Date };
type SoftDeletable = { deletedAt: Date | null };
type Auditable = { createdBy: string; updatedBy: string };

type FullEntity = Timestamped & SoftDeletable & Auditable;

let article: FullEntity = {
  createdAt: new Date(),
  updatedAt: new Date(),
  deletedAt: null,
  createdBy: "admin",
  updatedBy: "editor"
};


2. قواعد دمج البيانات متعددة الأنواع

(1) الخصائص التي تحمل الاسم نفسه — أخذ تقاطع الأنواع

عندما يكون لدى نوعين خصائص تحمل الاسم نفسه ولكنهما من أنواع مختلفة، تكون نتيجة التقاطع هي تقاطع هذين النوعين:

TYPESCRIPT
type A = { value: string | number };
type B = { value: string | boolean };
type C = A & B;

// C 's value Type = (string | number) & (string | boolean) = string
let c: C = { value: "hello" };     // ✅ string It is the intersection
// let c2: C = { value: 42 };      // ❌ number Not in the intersection
// let c3: C = { value: true };    // ❌ boolean Not in the intersection

(2) الخصائص التي تحمل الاسم نفسه — تؤدي الأنواع غير المتوافقة إلى ظهور خطأ من نوع «never»

عندما لا تتداخل خصائص نوعين يحملان الاسم نفسه، تكون النتيجة never — فمن المستحيل أن توجد مثل هذه القيمة:

TYPESCRIPT
type A = { id: string };
type B = { id: number };
type C = A & B;

// C 's id Type = string & number = never
// No value can be both string and number
// let c: C = { id: "x" };  // ❌ You cannot string Assigned never
⚠️ ملاحظة: لا يقوم TypeScript بإصدار خطأ بشكل صريح عندما يؤدي التداخل بين الأنواع إلى never — بل يكتفي بجعل النوع غير متاح. هذا هو الفرق الرئيسي بين الأنواع المتقاطعة وinterface extends: extends يُصدر خطأً عند اكتشاف تعارض، بينما تؤدي الأنواع المتقاطعة & بصمت إلى never.

(3) تقاطع أنواع الدوال

عندما تتقاطع أنواع الدوال، تُعتبر المعلمات هي تقاطعها (مما يؤدي عادةً إلى never):

TYPESCRIPT
type StringHandler = (value: string) => void;
type NumberHandler = (value: number) => void;
type MixedHandler = StringHandler & NumberHandler;

// MixedHandler parameters = string & number = never
// In fact, there is no value that can satisfy both signatures at the same time.
💡 نصيحة: نادرًا ما يُستخدم تحميل الدوال في عمليات التطوير العادية. إذا كنت بحاجة إلى دالة «قادرة على التعامل مع أنواع متعددة»، فاستخدم أنواع الاتحاد (union types) بدلاً من تحميل الدوال.



3. الفرق بين النوع المتقاطع وinterface extends

(1) المقارنة النحوية

TYPESCRIPT
// interface extends
interface Person {
  name: string;
}
interface Employee extends Person {
  employeeId: string;
}

// type Crossing
type Person2 = { name: string };
type Employee2 = Person2 & { employeeId: string };

(2) مقارنة بين أساليب حل النزاعات

السيناريو امتداد الواجهة تقاطع الأنواع &
توافق أنواع السمات التي تحمل الاسم نفسه ✅ النوع الفرعي يلغي النوع الأصلي أخذ التقاطع
أنواع الخصائص غير المتوافقة التي تحمل الاسم نفسه ❌ خطأ في الترجمة يُنشئ never دون إظهار أي رسالة
الوراثة المتعددة الوراثة الفردية فقط يُسمح بالوراثة المتقاطعة المتعددة
تم دمج البيان أؤيد لا أؤيد

(3) توصيات الاختيار

TYPESCRIPT
// Use extends scenario — Compile-time conflict detection is required
interface BaseConfig {
  host: string;
  port: number;
}

interface DevConfig extends BaseConfig {
  debug: boolean;    // ✅ New Properties
  // host: number;   // ❌ Compilation error——Parent Type host: string Conflict
}

// Using Intercut Scenes——Requires flexible combinations of multiple types
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithAudit = { createdBy: string; updatedBy: string };
type FullRecord = BaseConfig & WithTimestamps & WithAudit;
// Quick Combinations,No need to define an intermediate step interface


4. الأنماط الشائعة لتركيبات الأنواع

(1) النمط 1: Mixin

"دمج" قدرات إضافية في كائن باستخدام أنواع الواجهات:

TYPESCRIPT
type WithId = { id: number };
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithSoftDelete = { deletedAt: Date | null };
type WithAudit = { createdBy: string; updatedBy: string };

// Freely combine different abilities
type BaseEntity = WithId & WithTimestamps;
type FullEntity = WithId & WithTimestamps & WithSoftDelete & WithAudit;

interface Article extends BaseEntity {
  title: string;
  content: string;
}

let article: Article = {
  id: 1,
  createdAt: new Date(),
  updatedAt: new Date(),
  title: "TypeScript Getting Started",
  content: "TypeScript is a superset of JavaScript..."
};

(2) النموذج 2: الجمع بين الشروط

حدد ما إذا كنت تريد استبعاد نوع معين بناءً على الشروط:

TYPESCRIPT
type EntityWithOptional<T, TExtra> = T & Partial<TExtra>;

interface User {
  id: number;
  name: string;
}

interface UserProfile {
  avatar: string;
  bio: string;
}

// User + Optional Profile
type UserWithOptionalProfile = EntityWithOptional<User, UserProfile>;
// { id: number; name: string; avatar?: string; bio?: string }

(3) النموذج الثالث: الأنواع ذات العلامات التجارية

استخدم الأنواع المركبة لـ«تسمية» الأنواع الأولية ومنع إساءة استخدامها:

TYPESCRIPT
type USD = number & { __brand: "USD" };
type EUR = number & { __brand: "EUR" };

function createUSD(amount: number): USD {
  return amount as USD;
}

function createEUR(amount: number): EUR {
  return amount as EUR;
}

let price: USD = createUSD(100);
let cost: EUR = createEUR(80);

// price = cost;          // ❌ EUR Cannot be assigned to USD
// price + cost;          // ❌ Cannot be combined

function addUSD(a: USD, b: USD): USD {
  return (a + b) as USD;  // Calculations can be made within the same currency
}

let total = addUSD(price, createUSD(50));
console.log(total);  // 150

▶ مثال: تركيبات التكوين الآمنة من حيث النوع

TYPESCRIPT
// Basic Configuration
type BaseConfig = {
  host: string;
  port: number;
};

// Additional Development Environment Configuration
type DevConfig = BaseConfig & {
  debug: true;
  mockApi: boolean;
};

// Additional Configuration for the Production Environment
type ProdConfig = BaseConfig & {
  debug: false;
  ssl: boolean;
  maxConnections: number;
};

function createDevConfig(): DevConfig {
  return { host: "localhost", port: 3000, debug: true, mockApi: true };
}

function createProdConfig(): ProdConfig {
  return { host: "api.example.com", port: 443, debug: false, ssl: true, maxConnections: 100 };
}

let dev = createDevConfig();
let prod = createProdConfig();

console.log(`Development:${dev.host}:${dev.port} (mock: ${dev.mockApi})`);
console.log(`Production:${prod.host}:${prod.port} (ssl: ${prod.ssl})`);
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
Development:localhost:3000 (mock: true)
Production:api.example.com:443 (ssl: true)


5. تقنيات متقدمة للعمليات بين الأنواع

(1) التحويل التكراري للأنواع

TYPESCRIPT
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object
    ? T[K] extends Array<any>
      ? T[K]
      : DeepPartial<T[K]>
    : T[K];
};

interface Config {
  server: { host: string; port: number };
  database: { url: string; pool: { min: number; max: number } };
}

type PartialConfig = DeepPartial<Config>;
let cfg: PartialConfig = {};  // All levels are available

(2) التقاطعات في الدوال العامة

TYPESCRIPT
function merge<T extends object, U extends object>(a: T, b: U): T & U {
  return { ...a, ...b };
}

let person = merge({ name: "Charlie" }, { age: 20 });
// person Type:{ name: string } & { age: number }

(3) التقاطعات في الأنواع الشرطية

TYPESCRIPT
type AddTimestamps<T> = T & { createdAt: Date; updatedAt: Date };

interface User {
  name: string;
  email: string;
}

type TimestampedUser = AddTimestamps<User>;
// { name: string; email: string; createdAt: Date; updatedAt: Date }

▶ مثال: أنواع الأدوات المبنية باستخدام التقاطع

الناتج:

TEXT 📖 للعرض فقط
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)
TYPESCRIPT
type WithId = { id: number };
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithSoftDelete = { deletedAt: Date | null };

type Entity = WithId & WithTimestamps & WithSoftDelete;

type Creatable<T> = Omit<T, "id" | "createdAt" | "updatedAt" | "deletedAt">;
type Updatable<T> = Partial<Omit<T, "id">>;

interface Product extends Entity {
  name: string;
  price: number;
}

type CreateProduct = Creatable<Product>;
// { name: string; price: number }

type UpdateProduct = Updatable<Product>;
// { name?: string; price?: number; createdAt?: Date; ... }

let newProduct: CreateProduct = { name: "Widget", price: 9.99 };
let update: UpdateProduct = { price: 12.99 };
console.log(update.price);  // 12.99

الناتج:

TEXT 📖 للعرض فقط
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)

▶ مثال: الأنواع ذات العلامات لأمان المجال

الناتج:

TEXT 📖 للعرض فقط
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)
TYPESCRIPT
type UserId = number & { __brand: "UserId" };
type OrderId = number & { __brand: "OrderId" };

function createUserId(n: number): UserId { return n as UserId; }
function createOrderId(n: number): OrderId { return n as OrderId; }

let uid = createUserId(42);
let oid = createOrderId(99);

// uid = oid;  // ❌ OrderId is not assignable to UserId

function findUser(id: UserId): string {
  return `User #${id}`;
}

console.log(findUser(uid));        // "User #42"
// console.log(findUser(oid));     // ❌ prevents accidental misuse

الناتج:

TEXT 📖 للعرض فقط
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)


❓ أسئلة شائعة

س كيف يمكنني حل مشكلة خطأ "never" الناتج عن تداخل الأنواع؟
ج قم بإزالة العناصر المتداخلة تدريجيًا لمعرفة أي نوعين يتعارضان. أحد الأسباب الشائعة هو وجود أنواع خصائص غير متوافقة تحمل الاسم نفسه (على سبيل المثال، string & number). نوصي باستخدام interface extends بدلاً من التقاطعات — حيث يُطلق extends خطأً فورًا عند حدوث تعارض، مما يسهل تحديد المشكلة بدقة.
س هل يمكن أن يحل «التعريف المتقاطع» محل «الوراثة»؟
ج يمكن ذلك في معظم الحالات، لكنهما ليسا متكافئين تمامًا. يوفر interface extends الكشف عن التضارب في وقت التحويل البرمجي، ودمج الإعلانات، ودعم class implements. أما التجميع بين الأنواع فهو أكثر مرونة ولكنه يفتقر إلى الكشف عن التضارب. نوصي باستخدام extends لدمج أنواع الكائنات، واستخدام التجميع بين الأنواع للدمج البسيط والسريع.
س هل الأنواع ذات العلامات مفيدة في عملية التطوير الفعلية؟
ج إنها مفيدة جدًّا عندما تحتاج إلى التمييز بين الأنواع التي لها نفس البنية ولكن معاني مختلفة. ومن الأمثلة النموذجية على ذلك العملات (الدولار الأمريكي مقابل اليورو)، ومعرّفات الهوية (UserId مقابل OrderId)، ووحدات القياس (المتر مقابل القدم). تمنع الأنواع ذات العلامات التجارية الخلط العرضي وتكتشف الأخطاء — مثل معاملة اليورو على أنه دولار — في مرحلة التحويل البرمجي.
س هل يمكن استخدام الأنواع الاختيارية والأنواع الترابطية معًا؟
ج نعم. type T = (A & B) | (C & D) تعني «إما أن يتم استيفاء A و B، أو أن يتم استيفاء C و D». تحدد الأقواس الأسبقية — & لها أسبقية أعلى من |، لذا فإن A & B | C & D تساوي (A & B) | (C & D).

📖 ملخص

📝 تمارين

  1. المشكلة الأساسية (الصعوبة ⭐): عرّف نوعين، هما WithId وWithTimestamps، واجمعهما باستخدام نوع «تقاطع» لتشكيل BaseEntity، ثم أنشئ كائنًا يستوفي BaseEntity.
  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): قم بتنفيذ أنواع العلامات التجارية UserId = number & { __brand: "UserId" } وOrderId = number & { __brand: "OrderId" }. اكتب دوال لإنشاء كل نوع من أنواع المعرّفات، وتأكد من أنه لا يمكن تعيين أي منها للآخر.
  3. مشكلة التحدي (الصعوبة ⭐⭐⭐): قم بتنفيذ نوع الأداة Overwrite<T, U> — استبدل الخصائص في T التي تحمل نفس الاسم باستخدام الخصائص الموجودة في U، مع الحفاظ على الخصائص التي تحمل أسماء مختلفة. تلميح: استخدم نوع التعيين + نوع التقاطع.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%