TypeScript

الواجهات في TypeScript — دليل شامل Interfaces 2026

📅 2026-10-03⏱ 9 دقائق قراءة
في المقال السابق، تعلمت الأنواع الأساسية في TypeScript. الآن سنتعمق في **الواجهات (Interfaces)** — وهي إحدى أقوى ميزات TypeScript لتعريف **هياكل البيانات**. في هذا الدليل العملي، سنأخذك خطوة بخطوة لفهم الواجهات، والفرق بينها وبين `type`، وكيفية استخدامها في مشاريعك، مع تمارين وحلول. ## ما هي الواجهة (Interface)؟ الواجهة هي **عقد (Contract)** يحدد **شكل** الكائن — ما الخصائص التي يجب أن يحتويها، وما أنواعها. **تشبيه بسيط:** تخيل أن الواجهة هي "قالب" أو "مخطط" لبناء منزل. أي منزل يُبنى من هذا القالب يجب أن يحتوي على: غرفة نوم، مطبخ، حمام. **مثال:** ```typescript interface User { name: string; age: number; email: string; } const user: User = { name: "أحمد", age: 25, email: "[email protected]", }; ``` **شرح:** - **`interface User`:** تعريف واجهة باسم `User`. - **`{ name: string; ... }`:** الخصائص المطلوبة. - **`: User`:** المتغير `user` يجب أن يطابق واجهة `User`. **⚠️ مهم:** إذا نسيت خاصية أو كتبتها بنوع خاطئ، TypeScript ستعطي خطأ: ```typescript const user: User = { name: "أحمد", age: "25", // ❌ خطأ: يجب أن يكون number // ❌ خطأ: email مفقود }; ``` ## لماذا نحتاج الواجهات؟ - **الوضوح:** الكود يوثق نفسه. - **الأمان:** تكتشف الأخطاء قبل التشغيل. - **إعادة الاستخدام:** عرّف الواجهة مرة، استخدمها في أماكن كثيرة. - **الأدوات:** إكمال تلقائي (IntelliSense) أفضل. - **التعاون:** الفريق يفهم بنية البيانات. ## الخصائص الاختيارية (Optional Properties) استخدم `?` لجعل خاصية اختيارية: ```typescript interface User { name: string; age: number; email?: string; // اختياري } const user1: User = { name: "أحمد", age: 25, }; // ✅ صحيح const user2: User = { name: "محمد", age: 30, email: "[email protected]", }; // ✅ صحيح ``` ## خصائص القراءة فقط (Readonly) استخدم `readonly` لمنع التعديل بعد الإنشاء: ```typescript interface Config { readonly apiKey: string; readonly baseUrl: string; timeout: number; // قابل للتعديل } const config: Config = { apiKey: "abc123", baseUrl: "https://api.example.com", timeout: 5000, }; config.timeout = 10000; // ✅ مسموح // config.apiKey = "new-key"; // ❌ خطأ ``` ## طرق (Methods) في الواجهات يمكنك تعريف دوال داخل الواجهة: ```typescript interface Calculator { add(a: number, b: number): number; subtract(a: number, b: number): number; } const calc: Calculator = { add(a, b) { return a + b; }, subtract(a, b) { return a - b; }, }; console.log(calc.add(5, 3)); // 8 console.log(calc.subtract(5, 3)); // 2 ``` ## الفهارس (Index Signatures) إذا كنت لا تعرف أسماء الخصائص مسبقاً: ```typescript interface StringMap { [key: string]: string; } const colors: StringMap = { red: "#FF0000", green: "#00FF00", blue: "#0000FF", }; console.log(colors.red); // #FF0000 console.log(colors["green"]); // #00FF00 ``` **مثال عملي:** قاموس (Dictionary): ```typescript interface Dictionary { [key: string]: number; } const ages: Dictionary = { ahmed: 25, mohamed: 30, ali: 22, }; console.log(ages.ahmed); // 25 ``` ## توسيع الواجهات (Interface Extension) يمكنك بناء واجهة على واجهة أخرى باستخدام `extends`: ```typescript interface Person { name: string; age: number; } interface Employee extends Person { company: string; salary: number; } const emp: Employee = { name: "أحمد", age: 25, company: "Google", salary: 50000, }; ``` **توسيع متعدد:** ```typescript interface A { a: string; } interface B { b: string; } interface C extends A, B { c: string; } const obj: C = { a: "a", b: "b", c: "c", }; ``` ## تنفيذ الواجهات (implements) الفئات (Classes) يمكنها تنفيذ الواجهات: ```typescript interface Animal { name: string; makeSound(): void; } class Dog implements Animal { name: string; constructor(name: string) { this.name = name; } makeSound(): void { console.log(`${this.name} يقول: هاو هاو!`); } } const dog = new Dog("روكي"); dog.makeSound(); // روكي يقول: هاو هاو! ``` ## الفرق بين `interface` و `type` هذا سؤال شائع جداً. دعنا نوضح: <table> <thead> <tr> <th>المعيار</th> <th>interface</th> <th>type</th> </tr> </thead> <tbody> <tr> <td><strong>تعريف الكائنات</strong></td> <td>✅ ممتاز</td> <td>✅ ممتاز</td> </tr> <tr> <td><strong>Union Types</strong></td> <td>❌ لا يدعم</td> <td>✅ يدعم</td> </tr> <tr> <td><strong>Intersection</strong></td> <td>✅ عبر extends</td> <td>✅ عبر &amp;</td> </tr> <tr> <td><strong>إعادة التعريف (Declaration Merging)</strong></td> <td>✅ يدعم</td> <td>❌ لا يدعم</td> </tr> <tr> <td><strong>implements في Classes</strong></td> <td>✅ يدعم</td> <td>✅ يدعم</td> </tr> <tr> <td><strong>Tuple</strong></td> <td>❌ لا يدعم</td> <td>✅ يدعم</td> </tr> </tbody> </table> ### متى تستخدم `interface`؟ - لتعريف **شكل الكائنات** (Objects). - للفئات (Classes) التي تنفذ الواجهة. - عندما تحتاج إلى **إعادة تعريف** (Declaration Merging). ### متى تستخدم `type`؟ - لـ **Union Types** (`string | number`). - لـ **Tuple** (`[string, number]`). - للأنواع البدائية (`type ID = string | number`). - للأنواع المعقدة (Conditional, Mapped, ...). ### مثال مقارن: ```typescript // interface interface User { name: string; age: number; } // type type User = { name: string; age: number; }; ``` **كلاهما يعمل بنفس الطريقة** لتحديد شكل الكائن. **لكن:** ```typescript // type يدعم Union type Status = "pending" | "approved" | "rejected"; // interface لا يدعم Union // interface Status = "pending" | "approved"; // ❌ خطأ ``` ### إعادة التعريف (Declaration Merging): ```typescript // interface: يمكن إعادة تعريفها interface User { name: string; } interface User { age: number; } // النتيجة: User له name و age const user: User = { name: "أحمد", age: 25, }; // type: لا يمكن إعادة تعريفها type Person = { name: string }; // type Person = { age: number }; // ❌ خطأ ``` ## الواجهات للدوال (Call Signatures) يمكنك تعريف واجهة تمثل دالة: ```typescript interface GreetFunction { (name: string): string; } const greet: GreetFunction = (name) => `مرحباً ${name}`; console.log(greet("أحمد")); // مرحباً أحمد ``` ## الواجهات للفئات (Construct Signatures) ```typescript interface PersonConstructor { new (name: string, age: number): Person; } interface Person { name: string; age: number; } class PersonImpl implements Person { constructor(public name: string, public age: number) {} } const PersonClass: PersonConstructor = PersonImpl; const person = new PersonClass("أحمد", 25); ``` ## تمارين عملية ### تمرين 1: واجهة مستخدم عرّف واجهة `User` تحتوي على: - id (number) - name (string) - email (string) - isActive (boolean) **الحل:** ```typescript interface User { id: number; name: string; email: string; isActive: boolean; } const user: User = { id: 1, name: "أحمد", email: "[email protected]", isActive: true, }; ``` ### تمرين 2: خصائص اختيارية عرّف واجهة `Product` تحتوي على: - name (string) - price (number) - description (string, اختياري) - discount (number, اختياري) **الحل:** ```typescript interface Product { name: string; price: number; description?: string; discount?: number; } const product1: Product = { name: "لابتوب", price: 5000, }; const product2: Product = { name: "هاتف", price: 2000, description: "هاتف ذكي", discount: 10, }; ``` ### تمرين 3: توسيع الواجهات عرّف `Person`، ثم `Student` يرث منها. **الحل:** ```typescript interface Person { name: string; age: number; } interface Student extends Person { studentId: string; major: string; } const student: Student = { name: "أحمد", age: 20, studentId: "S12345", major: "علوم حاسب", }; ``` ### تمرين 4: دالة في واجهة عرّف واجهة `Shape` لها دالة `area()`. **الحل:** ```typescript interface Shape { area(): number; } const circle: Shape = { area() { return Math.PI * 5 * 5; }, }; console.log(circle.area()); // 78.5398... ``` ### تمرين 5: قاموس عرّف `Dictionary` يخزن `string -> number`. **الحل:** ```typescript interface Dictionary { [key: string]: number; } const scores: Dictionary = { ahmed: 95, mohamed: 88, ali: 92, }; console.log(scores.ahmed); // 95 ``` ### تمرين 6: implements أنشئ واجهة `Vehicle` وفئة `Car` تنفذها. **الحل:** ```typescript interface Vehicle { brand: string; speed: number; accelerate(): void; } class Car implements Vehicle { brand: string; speed: number; constructor(brand: string, speed: number) { this.brand = brand; this.speed = speed; } accelerate(): void { this.speed += 10; console.log(`${this.brand} تسارع إلى ${this.speed} كم/س`); } } const car = new Car("تويوتا", 100); car.accelerate(); // تويوتا تسارع إلى 110 كم/س ``` ### تمرين 7: واجهة API Response عرّف واجهة تمثل استجابة API. **الحل:** ```typescript interface ApiResponse<T> { success: boolean; data: T; message?: string; } interface User { id: number; name: string; } const response: ApiResponse<User> = { success: true, data: { id: 1, name: "أحمد" }, message: "تم بنجاح", }; ``` **⚠️ ملاحظة:** هذا مثال متقدم قليلاً (Generics). سنتعلمه في مقال لاحق. ### تمرين 8: الفرق بين interface و type عرّف نوع `Status` باستخدام `type`، ونوع `User` باستخدام `interface`. **الحل:** ```typescript type Status = "active" | "inactive" | "pending"; interface User { id: number; name: string; status: Status; } const user: User = { id: 1, name: "أحمد", status: "active", }; ``` ## حل المشاكل الشائعة ### 🔴 المشكلة 1: `Property 'X' is missing` **السبب:** نسيت خاصية مطلوبة. ```typescript interface User { name: string; age: number; } const user: User = { name: "أحمد", // ❌ age مفقود }; ``` **الحل:** أضف الخاصية المفقودة، أو اجعلها اختيارية `?`. ### 🔴 المشكلة 2: `Type 'X' is not assignable to type 'Y'` **السبب:** نوع خاصية مختلف. ```typescript interface User { age: number; } const user: User = { age: "25", // ❌ يجب أن يكون number }; ``` **الحل:** استخدم النوع الصحيح. ### 🔴 المشكلة 3: `Cannot assign to 'X' because it is a read-only property` **السبب:** حاولت تعديل خاصية `readonly`. **الحل:** احذف `readonly` إذا كنت تريد التعديل، أو لا تعدّل. ### 🔴 المشكلة 4: `Interface 'X' incorrectly extends interface 'Y'` **السبب:** توسيع واجهة بنوع غير متوافق. **الحل:** تأكد من توافق الأنواع في الواجهة الفرعية. ## جدول الأوامر الأساسية <table> <thead> <tr> <th>الأمر</th> <th>الوظيفة</th> </tr> </thead> <tbody> <tr> <td><code>interface X { }</code></td> <td>تعريف واجهة</td> </tr> <tr> <td><code>property?: type</code></td> <td>خاصية اختيارية</td> </tr> <tr> <td><code>readonly property</code></td> <td>خاصية للقراءة فقط</td> </tr> <tr> <td><code>interface X extends Y</code></td> <td>توسيع واجهة</td> </tr> <tr> <td><code>class X implements Y</code></td> <td>تنفيذ واجهة</td> </tr> <tr> <td><code>[key: string]: type</code></td> <td>فهرس ديناميكي</td> </tr> </tbody> </table> ## قائمة تحقق نهائية <table> <thead> <tr> <th>المهمة</th> <th>الحالة</th> </tr> </thead> <tbody> <tr> <td>فهم الواجهات وكيفية تعريفها</td> <td>⬜</td> </tr> <tr> <td>استخدام الخصائص الاختيارية (?)</td> <td>⬜</td> </tr> <tr> <td>استخدام readonly</td> <td>⬜</td> </tr> <tr> <td>تعريف دوال في الواجهات</td> <td>⬜</td> </tr> <tr> <td>توسيع الواجهات (extends)</td> <td>⬜</td> </tr> <tr> <td>تنفيذ الواجهات (implements)</td> <td>⬜</td> </tr> <tr> <td>فهم الفرق بين interface و type</td> <td>⬜</td> </tr> <tr> <td>حل التمارين الثمانية</td> <td>⬜</td> </tr> </tbody> </table> ## ماذا بعد هذا المقال؟ الآن بعد أن أتقنت الواجهات، أنت جاهز للمقال التالي: 1. **الدوال مع الأنواع** — تعمق في أنواع الدوال. 2. **الفئات (Classes)** — البرمجة كائنية التوجه. 3. **الأدوية (Generics)** — الأنواع العامة. ## الخلاصة في هذا المقال، تعلمت: - ✅ ما هي الواجهات ولماذا نحتاجها. - ✅ تعريف الواجهات والخصائص الاختيارية. - ✅ readonly و Index Signatures. - ✅ الطرق (Methods) في الواجهات. - ✅ توسيع الواجهات (extends). - ✅ تنفيذ الواجهات (implements). - ✅ الفرق بين interface و type. **تذكر:** الواجهات هي أساس المشاريع الكبيرة في TypeScript. أتقنها جيداً. **هل أتقنت الواجهات؟ شاركنا في التعليقات!**