الأسماء المستعارة للأنواع والواجهات
الأسماء المستعارة للأنواع والواجهات
توفر TypeScript طريقتين رئيسيتين لتعريف الأنواع المخصصة: الأسماء المستعارة للأنواع (Type Aliases) والواجهات (Interfaces). كلاهما يسمح لك بإنشاء تعريفات أنواع قابلة لإعادة الاستخدام، لكن لديهما اختلافات دقيقة في القدرات وحالات الاستخدام. فهم متى تستخدم كل منهما أمر بالغ الأهمية لكتابة كود TypeScript نظيف وقابل للصيانة.
الأسماء المستعارة للأنواع
الاسم المستعار للنوع ينشئ اسماً جديداً لأي نوع. تقوم بتعريفه باستخدام الكلمة المفتاحية type متبوعة باسم الاسم المستعار وتعريف النوع.
// اسم مستعار بسيط للنوع البدائي
type UserID = string | number;
// اسم مستعار لنوع لشكل الكائن
type User = {
id: UserID;
name: string;
email: string;
age?: number;
};
// استخدام الاسم المستعار للنوع
const user1: User = {
id: 123,
name: 'John Doe',
email: 'john@example.com'
};
const user2: User = {
id: 'abc-456',
name: 'Jane Smith',
email: 'jane@example.com',
age: 28
};
الأسماء المستعارة للأنواع المعقدة
تتفوق الأسماء المستعارة للأنواع في تمثيل تركيبات الأنواع المعقدة:
// اسم مستعار لنوع الاتحاد
type Status = 'pending' | 'approved' | 'rejected';
// اسم مستعار لنوع المجموعة
type Coordinate = [number, number];
// اسم مستعار لنوع الدالة
type MathOperation = (a: number, b: number) => number;
// كائن معقد مع أنواع متداخلة
type Product = {
id: string;
name: string;
price: number;
category: 'electronics' | 'clothing' | 'food';
tags: string[];
metadata: {
createdAt: Date;
updatedAt: Date;
views: number;
};
};
// استخدام اسم مستعار لنوع الدالة
const add: MathOperation = (a, b) => a + b;
const multiply: MathOperation = (a, b) => a * b;
// استخدام النوع المعقد
const laptop: Product = {
id: 'prod-001',
name: 'MacBook Pro',
price: 2499,
category: 'electronics',
tags: ['computer', 'apple', 'laptop'],
metadata: {
createdAt: new Date(),
updatedAt: new Date(),
views: 1250
}
};
الواجهات
الواجهة هي طريقة لتعريف شكل نوع الكائن. تستخدم الواجهات الكلمة المفتاحية interface وهي مصممة خصيصاً لأشكال الكائنات.
// تعريف الواجهة
interface Person {
firstName: string;
lastName: string;
age: number;
email?: string; // خاصية اختيارية
readonly id: string; // لا يمكن تعديلها بعد الإنشاء
}
// استخدام الواجهة
const person: Person = {
id: 'person-123',
firstName: 'Alice',
lastName: 'Johnson',
age: 30
};
// person.id = 'new-id'; // خطأ: لا يمكن التعيين إلى 'id' لأنها خاصية للقراءة فقط
// دالة في الواجهة
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
const calc: Calculator = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
readonly للخصائص التي يجب تعيينها فقط أثناء التهيئة. هذا يساعد على منع التعديلات العرضية ويجعل الكود الخاص بك أكثر قابلية للتنبؤ.
توسيع الواجهات
واحدة من أقوى ميزات الواجهات هي القدرة على توسيع واجهات أخرى باستخدام الكلمة المفتاحية extends. هذا يسمح لك ببناء هرميات أنواع معقدة.
// الواجهة الأساسية
interface Animal {
name: string;
age: number;
makeSound(): void;
}
// الواجهة الموسعة
interface Dog extends Animal {
breed: string;
wagTail(): void;
}
// واجهة تمتد من واجهات متعددة
interface Bird {
wingspan: number;
canFly: boolean;
}
interface FlyingAnimal extends Animal, Bird {
altitude: number;
}
// التطبيق
const myDog: Dog = {
name: 'Buddy',
age: 5,
breed: 'Golden Retriever',
makeSound: () => console.log('Woof!'),
wagTail: () => console.log('*tail wagging*')
};
const eagle: FlyingAnimal = {
name: 'Eagle',
age: 3,
wingspan: 2.5,
canFly: true,
altitude: 1000,
makeSound: () => console.log('Screech!')
};
أنواع التقاطع
أنواع التقاطع تجمع عدة أنواع في واحد باستخدام عامل &. النوع الناتج له جميع الخصائص من جميع الأنواع المدمجة.
// الأنواع الفردية
type Identifiable = {
id: string;
};
type Timestamped = {
createdAt: Date;
updatedAt: Date;
};
type Versioned = {
version: number;
};
// نوع التقاطع يجمع الثلاثة
type Entity = Identifiable & Timestamped & Versioned;
// الاستخدام
const article: Entity = {
id: 'article-001',
createdAt: new Date('2024-01-01'),
updatedAt: new Date('2024-02-14'),
version: 3
};
// التقاطع مع حروف الكائن
type Name = {
firstName: string;
lastName: string;
};
type Contact = {
email: string;
phone: string;
};
type Employee = Name & Contact & {
employeeId: string;
department: string;
};
const employee: Employee = {
firstName: 'Sarah',
lastName: 'Williams',
email: 'sarah@company.com',
phone: '+1234567890',
employeeId: 'EMP-789',
department: 'Engineering'
};
never.
الأسماء المستعارة للأنواع مقابل الواجهات: الاختلافات الرئيسية
بينما يمكن لكليهما تعريف أشكال الكائنات، هناك اختلافات مهمة بين الأسماء المستعارة للأنواع والواجهات:
// يمكن دمج الواجهات
interface Window {
title: string;
}
interface Window {
size: number;
}
// يتم دمج كلا الإعلانين في واحد
const window: Window = {
title: 'My Window',
size: 1024
};
// لا يمكن دمج الأسماء المستعارة للأنواع
type Config = {
theme: string;
};
// خطأ: معرف مكرر 'Config'
// type Config = {
// language: string;
// };
// توسيع الواجهة (باستخدام extends)
interface BaseUser {
id: string;
name: string;
}
interface AdminUser extends BaseUser {
permissions: string[];
}
// تقاطع الاسم المستعار للنوع (باستخدام &)
type BaseUserType = {
id: string;
name: string;
};
type AdminUserType = BaseUserType & {
permissions: string[];
};
// كلاهما يحققان نتائج مماثلة
const admin1: AdminUser = {
id: 'admin-1',
name: 'Admin User',
permissions: ['read', 'write', 'delete']
};
const admin2: AdminUserType = {
id: 'admin-2',
name: 'Admin User',
permissions: ['read', 'write', 'delete']
};
// الأسماء المستعارة للأنواع تعمل مع البدائيات، الاتحادات، المجموعات
type StringOrNumber = string | number;
type Coordinates = [number, number];
type Callback = () => void;
// الواجهات محدودة بأشكال الكائنات
// interface StringOrNumber = string | number; // خطأ!
// interface Coordinates = [number, number]; // خطأ!
// ومع ذلك، يمكن للواجهات تمثيل الدوال
interface CallbackInterface {
(): void;
}
// كلاهما يعمل لأنواع الدالة
const callback1: Callback = () => console.log('Called');
const callback2: CallbackInterface = () => console.log('Called');
متى تستخدم الأسماء المستعارة للأنواع مقابل الواجهات
إليك إرشادات عملية للاختيار بين الأسماء المستعارة للأنواع والواجهات:
// 1. تعريف أشكال الكائنات (خاصة واجهات برمجة التطبيقات العامة)
interface ApiResponse {
data: unknown;
status: number;
message: string;
}
// 2. قد تحتاج إلى دمج الإعلان
interface CustomWindow {
myCustomProperty: string;
}
// 3. العمل مع الفئات (الواجهات تعمل بشكل طبيعي مع implements)
interface Drawable {
draw(): void;
}
class Circle implements Drawable {
draw() {
console.log('Drawing circle');
}
}
// 4. بناء هرميات أنواع قابلة للتوسيع
interface Vehicle {
brand: string;
model: string;
}
interface Car extends Vehicle {
doors: number;
}
interface ElectricCar extends Car {
batteryCapacity: number;
}
// 1. تعريف أنواع الاتحاد
type Result = 'success' | 'error' | 'pending';
// 2. تعريف أنواع المجموعات
type RGB = [number, number, number];
// 3. معالجات الأنواع المعقدة
type Nullable<T> = T | null;
type ReadonlyProps<T> = {
readonly [K in keyof T]: T[K];
};
// 4. أنواع الدالة
type EventHandler = (event: Event) => void;
// 5. الأنواع المعينة وأنواع الأدوات المساعدة
type Optional<T> = {
[K in keyof T]?: T[K];
};
// 6. أنواع التقاطع
type UserWithTimestamps = User & Timestamped & Identifiable;
الجمع بين الأسماء المستعارة للأنواع والواجهات
يمكنك المزج والمطابقة بين الأسماء المستعارة للأنواع والواجهات في نفس قاعدة الكود:
// اسم مستعار للنوع للاتحاد
type UserRole = 'admin' | 'editor' | 'viewer';
// واجهة تستخدم اسم مستعار للنوع
interface UserAccount {
id: string;
username: string;
role: UserRole; // استخدام الاسم المستعار للنوع
metadata: UserMetadata;
}
// اسم مستعار للنوع يستخدم الواجهة
type UserMetadata = {
lastLogin: Date;
loginCount: number;
};
// واجهة تمتد مع التقاطع
interface PremiumUser extends UserAccount {
subscriptionLevel: 'basic' | 'pro' | 'enterprise';
}
// اسم مستعار للنوع يجمع الواجهة والنوع
type FullUserProfile = UserAccount & {
preferences: {
theme: 'light' | 'dark';
language: string;
};
notifications: boolean;
};
// التطبيق
const premiumUser: PremiumUser = {
id: 'user-001',
username: 'john_doe',
role: 'admin',
subscriptionLevel: 'pro',
metadata: {
lastLogin: new Date(),
loginCount: 150
}
};
const fullProfile: FullUserProfile = {
id: 'user-002',
username: 'jane_smith',
role: 'editor',
metadata: {
lastLogin: new Date(),
loginCount: 75
},
preferences: {
theme: 'dark',
language: 'en'
},
notifications: true
};
- أنشئ واجهة
Bookمع الخصائص:title،author،isbn(للقراءة فقط)، وpublishedYearاختيارية - أنشئ اسم مستعار للنوع
BookFormatكاتحاد من 'hardcover'، 'paperback'، 'ebook'، و'audiobook' - أنشئ واجهة
PhysicalBookالتي تمتد منBookوتضيفformat: BookFormatوpageCount: number - أنشئ اسم مستعار للنوع
DigitalBookباستخدام التقاطع الذي يجمعBookمع كائن يحتوي علىfileSize: numberوformat: 'ebook' | 'audiobook' - أنشئ نسخاً من كل من
PhysicalBookوDigitalBookمع بيانات نموذجية
الملخص
- الأسماء المستعارة للأنواع تستخدم الكلمة المفتاحية
typeويمكن أن تمثل أي نوع (البدائيات، الاتحادات، المجموعات، الكائنات، الدوال) - الواجهات تستخدم الكلمة المفتاحية
interfaceوهي متخصصة لأشكال الكائنات - الواجهات تدعم التوسيع مع الكلمة المفتاحية
extendsودمج الإعلان - الأسماء المستعارة للأنواع تدعم التقاطعات مع عامل
&لدمج الأنواع - اختر الواجهات لأشكال الكائنات، وواجهات برمجة التطبيقات العامة، والهرميات القابلة للتوسيع
- اختر الأسماء المستعارة للأنواع للاتحادات، المجموعات، معالجات الأنواع المعقدة، وأنواع الدوال
- يمكن دمج كلاهما في نفس قاعدة الكود لأقصى قدر من المرونة