TypeScript: واجهة TypeScript (interface)
آخر تحديث: 2026-08-26
تُعد الواجهة الطريقة الأساسية التي يحدد بها TypeScript بنية الكائن — فهي تصف «الخصائص والأساليب التي يجب أن يتضمنها الكائن»، لكنها لا توفر طريقة التنفيذ.
1. القواعد الأساسية لكتابة الواجهات
(1) تعريف واجهة
interface User {
name: string;
age: number;
email: string;
}
(2) استخدام الواجهة
let user: User = {
name: "Charlie",
age: 20,
email: "xiaoming@example.com"
};
(3) الواجهات كمعلمات دالة
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" }));
الناتج:
Hello,Diana!How old are you this year?22 years old。
(4) طرق وصف الواجهات
يمكن للواجهات أن تصف ليس فقط الخصائص، بل أيضًا توقيعات الطرق:
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) السمة الاختيارية ?
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
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 والواجهات
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) الوراثة الأحادية
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) الوراثة المتعددة
يمكن للواجهة أن ترث من واجهات متعددة في آن واحد:
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) تجاوز الخصائص
يمكن للواجهة الفرعية تجاوز أنواع الخصائص الخاصة بالواجهة الأصلية، ولكن يجب أن تكون متوافقة معها:
interface Base {
data: string | number;
}
interface Derived extends Base {
data: string; // ✅ Narrowing — string is a subtype of string | number
}
▶ مثال: إنشاء أنواع هرمية باستخدام الوراثة
// 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"
الناتج:
A red shape
A blue shape
4. دمج الإعلانات
من السمات الفريدة للواجهات أن الواجهات التي تحمل الاسم نفسه تدمج خصائصها تلقائيًا:
(1) الدمج الأساسي
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) قواعد الدمج
- يجب أن تكون الخصائص التي تحمل الاسم نفسه من نفس النوع؛ وإلا فسيتم الإبلاغ عن خطأ.
- دمج توقيعات الدوال في الدوال المُثقَّلة
- تظهر الواجهات التي يتم إعلانها لاحقًا في أعلى قائمة التحميل الزائد (وتتمتع بأولوية أعلى)
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) الاستخدام العملي: توسيع أنواع الجهات الخارجية
// 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. توقيعات الفهرس والواجهات
يمكن أيضًا استخدام توقيعات الفهرس في الواجهات:
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";
عندما يتواجد توقيع الفهرس والسمات المعروفة معًا، يجب أن تكون أنواع السمات المعروفة متوافقة:
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) متى تُستخدم الواجهة
- تحديد أشكال الكائنات (استجابات واجهة برمجة التطبيقات، وكائنات التكوين، ونماذج البيانات)
- يتطلب
extendsمن أجل التوريث - يتطلب إعلان دمج (لتوسيع نطاق نوع تابع لجهة خارجية)
- الفئة تُنفِّذ (تُنفِّذ الفئة واجهةً ما)
(2) متى يُستخدم type
- نوع المركب:
type Status = "active" | "inactive" - الأسماء المستعارة للأنواع الأساسية:
type ID = string | number - العمليات المتقدمة مثل الأنواع الشرطية وأنواع التعيين
- التعريفات الأساسية لأنواع الأدوات
interface لأنواع الكائنات وtype لأنواع الاتحاد والعمليات على الأنواع المتقدمة. هذان النوعان لا يستبعد أحدهما الآخر، ويمكن استخدامهما معًا في نفس المشروع.
▶ مثال: الخصائص الاختيارية وللقراءة فقط في الممارسة العملية
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)
الناتج:
undefined
▶ مثال: توسيع الواجهات لتكوين الأنواع
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}`);
الناتج:
Charlie: Hello TypeScript
❓ أسئلة شائعة
interface أم type؟interface لتعريف «شكل كائن»، واستخدم type لتعريف «اسم مستعار للنوع» أو «نوع الاتحاد». إذا كان لدى فريقك بالفعل قاعدة متفق عليها، فما عليك سوى اتباعها. في 90% من الحالات، يكون الاثنان متكافئين، لذا لا تفرط في التفكير في الأمر.type؟interface extends أن ترث أي اسم مستعار لنوع كائن محدد بواسطة type. وبالمقابل، يمكن لـ type أيضًا دمج الواجهات باستخدام الأنواع المتقاطعة &. وهما متوافقان تمامًا مع بعضهما البعض.type. interface Fn { (a: string): number } تعادل type Fn = (a: string) => number، والأخيرة أكثر إيجازًا ووضوحًا. نوصي باستخدام type لأنواع الدوال.📖 ملخص
- تصف الواجهة بنية الكائن — خصائصه، وأنواعها، وتوقيعات أساليبه
?تشير إلى خاصية اختيارية؛readonlyتشير إلى خاصية للقراءة فقط؛ ويمكن الجمع بينهما على النحو التالي:readonly x?: number- تُورث الواجهات باستخدام
extendsوتدعم كل من الوراثة الفردية والمتعددة؛ ويمكن للواجهات الفرعية تضييق أنواع خصائص واجهاتها الأم. - تجدر الإشارة إلى أن الدمج ميزة خاصة بالواجهات — حيث يتم دمج الواجهات التي تحمل الاسم نفسه تلقائيًا، ويُستخدم ذلك لتوسيع أنواع الجهات الخارجية.
- يُعد
interfaceمناسبًا لتعريف أشكال الكائنات، في حين يُعدtypeمناسبًا لأنواع الاتحاد والعمليات المتقدمة؛ وفي عمليات التطوير اليومية، غالبًا ما يُستخدم الاثنان بالتبادل.
📝 تمارين
- مشكلة أساسية (صعوبة ⭐): عرّف واجهة
Book(العنوان، المؤلف، عدد الصفحات، رقم ISBN؟)، وأنشئ كائنين من نوع «كتاب»، ثم اطبع كليهما. لاحظ أن رقم ISBN هو خاصية اختيارية. - مشكلة متقدمة (درجة الصعوبة ⭐⭐): عرّف الواجهة الأساسية
Shape(مع طريقتي color و area)، ثم عرّفCircleوRectangleبحيث يرث كل منهما من Shape ويضيف خصائصه الخاصة. اكتب دالة تأخذ معلمة من نوع Shape وتستدعي طريقة area. - تحدي (مستوى الصعوبة: ⭐⭐⭐): استخدم إعلانًا لإضافة طريقة
last(): T | undefinedإلى الواجهة المدمجةArray<T>. ثم استدعِ هذه الطريقة على مصفوفة فعلية، وفكر في سبب ضرورة إعلان الوحدة النمطية في هذه الحالة.