TypeScript: واجهة TypeScript (interface)

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

تُعد الواجهة الطريقة الأساسية التي يحدد بها TypeScript بنية الكائن — فهي تصف «الخصائص والأساليب التي يجب أن يتضمنها الكائن»، لكنها لا توفر طريقة التنفيذ.

1. القواعد الأساسية لكتابة الواجهات

(1) تعريف واجهة

TYPESCRIPT
interface User {
  name: string;
  age: number;
  email: string;
}

(2) استخدام الواجهة

TYPESCRIPT
let user: User = {
  name: "Charlie",
  age: 20,
  email: "xiaoming@example.com"
};

(3) الواجهات كمعلمات دالة

TYPESCRIPT
function greet(user: User): string {
  return `Hello,${user.name}!How old are you this year?${user.age} years old。`;
}

console.log(greet({ name: "Diana", age: 22, email: "hong@example.com" }));

الناتج:

TEXT 📖 للعرض فقط
Hello,Diana!How old are you this year?22 years old。

(4) طرق وصف الواجهات

يمكن للواجهات أن تصف ليس فقط الخصائص، بل أيضًا توقيعات الطرق:

TYPESCRIPT
interface Animal {
  name: string;
  speak(): string;
  move(distance: number): void;
}

let dog: Animal = {
  name: "Wangcai",
  speak() { return "Woof!"; },
  move(distance) { console.log(`${this.name} Moved ${distance}m`); }
};

console.log(dog.speak());   // "Woof!"
dog.move(10);               // "Wangcai Moved 10m"


2. الخصائص الاختيارية والخصائص للقراءة فقط

(1) السمة الاختيارية ?

TYPESCRIPT
interface Config {
  host: string;
  port: number;
  debug?: boolean;     // Optional
  timeout?: number;    // Optional
}

// Optional attributes may be omitted.
let config: Config = { host: "localhost", port: 3000 };
let config2: Config = { host: "localhost", port: 3000, debug: true };

(2) خاصية "للقراءة فقط" readonly

TYPESCRIPT
interface Point {
  readonly x: number;
  readonly y: number;
}

let point: Point = { x: 1, y: 2 };
// point.x = 10;  // ❌ Read-only properties cannot be modified.

(3) ReadonlyArray والواجهات

TYPESCRIPT
interface TodoList {
  readonly name: string;
  readonly items: readonly string[];   // items Both the array itself and its contents are read-only.
}

let todo: TodoList = {
  name: "Today's Tasks",
  items: ["Write code", "Test", "Deployment"]
};

// todo.items.push("New Task");  // ❌ Read-only arrays cannot be modified.
// todo.name = "Tomorrow's Tasks";     // ❌ Read-only properties cannot be modified.


3. وراثة الواجهة (extends)

يمكن للواجهات أن ترث من واجهات أخرى عبر extends لتمكين تكوين الأنواع وإعادة استخدامها:

(1) الوراثة الأحادية

TYPESCRIPT
interface Person {
  name: string;
  age: number;
}

interface Employee extends Person {
  employeeId: string;
  department: string;
}

let emp: Employee = {
  name: "Charlie",
  age: 28,
  employeeId: "E001",
  department: "Engineering Department"
};

(2) الوراثة المتعددة

يمكن للواجهة أن ترث من واجهات متعددة في آن واحد:

TYPESCRIPT
interface Serializable {
  serialize(): string;
}

interface Loggable {
  log(message: string): void;
}

interface Entity extends Serializable, Loggable {
  id: number;
}

let item: Entity = {
  id: 1,
  serialize() { return JSON.stringify({ id: this.id }); },
  log(message) { console.log(`[${this.id}] ${message}`); }
};

(3) تجاوز الخصائص

يمكن للواجهة الفرعية تجاوز أنواع الخصائص الخاصة بالواجهة الأصلية، ولكن يجب أن تكون متوافقة معها:

TYPESCRIPT
interface Base {
  data: string | number;
}

interface Derived extends Base {
  data: string;   // ✅ Narrowing — string is a subtype of string | number
}

▶ مثال: إنشاء أنواع هرمية باستخدام الوراثة

TYPESCRIPT
// Basic Interfaces
interface Shape {
  color: string;
}

// Extension Interface
interface Square extends Shape {
  sideLength: number;
}

interface Circle extends Shape {
  radius: number;
}

// Usage
let square: Square = { color: "Red", sideLength: 10 };
let circle: Circle = { color: "Blue", radius: 5 };

function describeShape(shape: Shape): string {
  return `One${shape.color}the graphic`;
}

console.log(describeShape(square));  // "A red shape"
console.log(describeShape(circle));  // "A blue shape"
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
A red shape
A blue shape


4. دمج الإعلانات

من السمات الفريدة للواجهات أن الواجهات التي تحمل الاسم نفسه تدمج خصائصها تلقائيًا:

(1) الدمج الأساسي

TYPESCRIPT
interface Window {
  title: string;
}

interface Window {
  count: number;
}

// Equivalent to:
// interface Window {
//   title: string;
//   count: number;
// }

let win: Window = { title: "Main Window", count: 3 };

(2) قواعد الدمج

TYPESCRIPT
interface Calculator {
  compute(a: number, b: number): number;
}

interface Calculator {
  compute(a: string, b: string): string;
}

// After the merger compute There are two overloads
let calc: Calculator = {
  compute(a: any, b: any): any {
    return a + b;
  }
};

console.log(calc.compute(1, 2));        // 3
console.log(calc.compute("a", "b"));    // "ab"

(3) الاستخدام العملي: توسيع أنواع الجهات الخارجية

TYPESCRIPT
// For the built-in Window Adding Custom Properties to an Interface
interface Window {
  myCustomProperty: string;
}

// It is now safe to use
// window.myCustomProperty = "hello";  // ✅
💡 لهذا السبب تستخدم interface بدلاً من type لتوسيع أنواع مكتبات الجهات الخارجية— type لا تدعم اتحاد الأنواع، بينما interface تدعمه.



5. توقيعات الفهرس والواجهات

يمكن أيضًا استخدام توقيعات الفهرس في الواجهات:

TYPESCRIPT
interface StringMap {
  [key: string]: string;
}

let translations: StringMap = {
  hello: "Hello",
  goodbye: "Goodbye",
  thanks: "Thank you"
};

// Add a new key-value pair
translations["sorry"] = "I'm sorry";

عندما يتواجد توقيع الفهرس والسمات المعروفة معًا، يجب أن تكون أنواع السمات المعروفة متوافقة:

TYPESCRIPT
interface Config {
  [key: string]: string | number;
  host: string;       // ✅ string is a subtype of string | number
  port: number;       // ✅ number is a subtype of string | number
  // debug: boolean;  // ❌ boolean No string | number subtypes of
}


6. الفرق بين الواجهات وأسماء الأنواع المستعارة

يمكن لكل من الواجهات والأنواع تعريف أنواع الكائنات، ولكن هناك الاختلافات التالية:

الميزة الواجهة النوع
نوع الكائن ✅ الاستخدام الرئيسي ✅ يمكن استخدامه أيضًا لـ
دمج البيانات ✅ مدعوم ❌ غير مدعوم
الميراث extends & من النوع المتقاطع
نوع الاتحاد ❌ لا يمكن تعريفه مباشرة type A = B | C
الأسماء المستعارة للأنواع الأساسية ❌ غير مسموح type ID = string
الخصائص المحسوبة ❌ غير مدعومة ✅ مدعومة
instanceof ✅ الفئة تُنفِّذ ❌ لا يمكن

(1) متى تُستخدم الواجهة

(2) متى يُستخدم type

📌 توصية: استخدم interface لأنواع الكائنات وtype لأنواع الاتحاد والعمليات على الأنواع المتقدمة. هذان النوعان لا يستبعد أحدهما الآخر، ويمكن استخدامهما معًا في نفس المشروع.


▶ مثال: الخصائص الاختيارية وللقراءة فقط في الممارسة العملية

TYPESCRIPT
interface UserProfile {
  readonly id: number;
  name: string;
  email: string;
  nickname?: string;
  readonly createdAt: Date;
}

let user: UserProfile = {
  id: 1,
  name: "Charlie",
  email: "charlie@example.com",
  createdAt: new Date()
};

user.name = "Diana";             // ✅ name is mutable
// user.id = 2;                  // ❌ readonly property
console.log(user.nickname);      // undefined (optional)
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
undefined

▶ مثال: توسيع الواجهات لتكوين الأنواع

TYPESCRIPT
interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface Owned {
  ownerId: number;
  ownerName: string;
}

interface Article extends Timestamped, Owned {
  title: string;
  content: string;
}

let article: Article = {
  title: "Hello TypeScript",
  content: "TypeScript is great...",
  createdAt: new Date(),
  updatedAt: new Date(),
  ownerId: 1,
  ownerName: "Charlie"
};

console.log(`${article.ownerName}: ${article.title}`);
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
Charlie: Hello TypeScript


❓ أسئلة شائعة

س أيهما يجب أن أستخدم — interface أم type؟
ج هناك قاعدة عامة بسيطة: استخدم interface لتعريف «شكل كائن»، واستخدم type لتعريف «اسم مستعار للنوع» أو «نوع الاتحاد». إذا كان لدى فريقك بالفعل قاعدة متفق عليها، فما عليك سوى اتباعها. في 90% من الحالات، يكون الاثنان متكافئين، لذا لا تفرط في التفكير في الأمر.
س هل يمكن لواجهة أن ترث نوعًا محددًا بواسطة type؟
ج نعم. يمكن لـ interface extends أن ترث أي اسم مستعار لنوع كائن محدد بواسطة type. وبالمقابل، يمكن لـ type أيضًا دمج الواجهات باستخدام الأنواع المتقاطعة &. وهما متوافقان تمامًا مع بعضهما البعض.
س ما هي مخاطر دمج الإعلانات؟
ج دمج الإعلانات هو إحدى ميزات الواجهات، لكنه ينطوي أيضًا على خطر محتمل — فإذا كانت هناك واجهتان تحملان الاسم نفسه وتحتويان على خصائص تحمل الاسم نفسه ولكن تختلفان في النوع، فسيحدث خطأ في الترجمة. في عملية التطوير الفعلية، يُستخدم دمج الإعلانات بشكل أساسي لتوسيع تعريفات الأنواع (.d.ts) للمكتبات الخارجية؛ لذا يجب تجنب تعريف واجهات تحمل الاسم نفسه في الكود الذي تكتبه يوميًا.
س هل يمكن لواجهة أن تصف نوع دالة؟
ج نعم، لكن من الأكثر طبيعية عمومًا استخدام type. interface Fn { (a: string): number } تعادل type Fn = (a: string) => number، والأخيرة أكثر إيجازًا ووضوحًا. نوصي باستخدام type لأنواع الدوال.

📖 ملخص

📝 تمارين

  1. مشكلة أساسية (صعوبة ⭐): عرّف واجهة Book (العنوان، المؤلف، عدد الصفحات، رقم ISBN؟)، وأنشئ كائنين من نوع «كتاب»، ثم اطبع كليهما. لاحظ أن رقم ISBN هو خاصية اختيارية.
  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): عرّف الواجهة الأساسية Shape (مع طريقتي color و area)، ثم عرّف Circle و Rectangle بحيث يرث كل منهما من Shape ويضيف خصائصه الخاصة. اكتب دالة تأخذ معلمة من نوع Shape وتستدعي طريقة area.
  3. تحدي (مستوى الصعوبة: ⭐⭐⭐): استخدم إعلانًا لإضافة طريقة last(): T | undefined إلى الواجهة المدمجة Array<T>. ثم استدعِ هذه الطريقة على مصفوفة فعلية، وفكر في سبب ضرورة إعلان الوحدة النمطية في هذه الحالة.
Web-Tutorial.com

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

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

100%