TypeScript: أنواع الكائنات في TypeScript
آخر تحديث: 2026-08-26
في لغة جافا سكريبت، تُعد الكائنات أكثر هياكل البيانات استخدامًا. ويتيح لك نوع الكائن في لغة تايب سكريبت وصف «الخصائص التي يجب أن يتضمنها الكائن وأنواعها» بدقة — وهذا يمثل جوهر نظام الأنواع المنظم الخاص بها.
1. أساسيات أنواع الكائنات
(1) أنواع الكائنات المجهولة
الطريقة الأبسط — تحديد بنية الكائن مباشرةً عند إعلان النوع:
let user: { name: string; age: number } = {
name: "Charlie",
age: 20
};
(2) استدلال الأنواع
عندما يتم تعيين قيمة حرفية لكائن إلى متغير، يقوم TypeScript باستنتاج النوع تلقائيًا:
let user = {
name: "Charlie",
age: 20
};
// Inferred as { name: string; age: number }
user.name = "Diana"; // ✅
user.age = 21; // ✅
// user.email = "x@x.com"; // ❌ Not included in the types of inferences email Properties
(3) بناء الجملة متعدد الأسطر
عندما يحتوي كائن ما على العديد من الخصائص، يُنصح باستخدام تنسيق متعدد الأسطر (مفصولة بفواصل منقوطة أو فواصل):
let product: {
id: number;
name: string;
price: number;
inStock: boolean;
} = {
id: 1,
name: "TypeScriptAuthoritative Guide",
price: 89.9,
inStock: true
};
▶ مثال: أنواع الكائنات لمعلمات الدالة
function createUser(user: { name: string; age: number; email: string }): string {
return `${user.name},${user.age} years old,Email:${user.email}`;
}
console.log(createUser({
name: "Charlie",
age: 20,
email: "xiaoming@example.com"
}));
الناتج:
Charlie,20 years old,Email:xiaoming@example.com
2. الخصائص الاختيارية
استخدم ? لتمييز سمة اختيارية — يمكن حذف هذه السمة:
(1) قواعد النحو
let user: { name: string; age?: number } = {
name: "Charlie"
// age This information is optional.
};
console.log(user.name); // "Charlie"
console.log(user.age); // undefined
(2) أنواع الصفات الاختيارية
تشمل أنواع السمات الاختيارية تلقائيًا undefined:
// age?: number Equivalent to age: number | undefined
// But there is an important difference:age?: number The property does not exist.,age: number | undefined The property must exist.
let config: { host: string; port?: number } = { host: "localhost" };
// config.port is undefined(The property does not exist either. ok 's)
let config2: { host: string; port: number | undefined } = { host: "localhost" };
// ❌ Error! port property must exist,Even if undefined You must also explicitly write
// Correct Way to Write It:{ host: "localhost", port: undefined }
(3) طرق آمنة للوصول إلى السمات الاختيارية
interface User {
name: string;
age?: number;
}
function getAgeText(user: User): string {
// ❌ Unsafe——If age Does not exist,undefined.toFixed() It crashes during runtime
// return user.age.toFixed(0) + " years old";
// ✅ Method 1:Check first
if (user.age !== undefined) {
return user.age.toFixed(0) + " years old";
}
return "Age unknown";
// ✅ Method 2:Provide a default value
// return (user.age ?? 0).toFixed(0) + " years old";
}
3. الخصائص المخصصة للقراءة فقط
استخدم العلامة readonly لتعيين خاصية ما على أنها للقراءة فقط — ولا يمكن تعديلها بعد التعيين:
let user: { readonly id: number; name: string } = {
id: 1,
name: "Charlie"
};
user.name = "Diana"; // ✅ General attributes can be modified
// user.id = 2; // ❌ Read-only properties cannot be modified.
(2) readonly هو مُعدِّل سطحي للقراءة فقط
let config: { readonly data: { value: number } } = {
data: { value: 42 }
};
// config.data = { value: 100 }; // ❌ data Cannot be reassigned
config.data.value = 100; // ✅ data The internal properties can still be modified!
Readonly<type> (متداخلة) أو التأكيد as const. سيتناول الدرس 19 أنواع الأدوات Readonly وPartial وRequired وغيرها.
4. توقيعات الفهرس
عندما يكون اسم خاصية كائن ما غير معروف، لكن نوع قيمة تلك الخاصية معروف، استخدم توقيع الفهرس:
(1) توقيع مؤشر السلسلة
let scores: { [key: string]: number } = {
Chinese Language: 90,
Mathematics: 95,
English: 88
};
console.log(scores["Mathematics"]); // 95
// Add a New Property——As long as it is string key + number Any value will do.
scores["Physics"] = 85; // ✅
(2) التوقيعات الرقمية المفهرسة
let arr: { [index: number]: string } = {
0: "first",
1: "second",
2: "third"
};
console.log(arr[1]); // "second"
(3) القيود المفروضة على التوقيعات المؤشرية
يتطلب توقيع الفهرس أن تكون أنواع جميع السمات المعروفة أنواعًا فرعية لنوع قيمة الفهرس:
// ❌ Error: name is string,However, index signatures require that all values be number
// let config: { [key: string]: number; name: string } = { name: "test" };
// ✅ Correct:All value types are consistent
let config: { [key: string]: string; name: string } = { name: "test" };
(4) توقيعات الفهرس في القيم الثابتة للنماذج (TypeScript 4.4+)
let css: { [key: `--${string}`]: string } = {
"--primary-color": "#4A90D9",
"--font-size": "14px"
};
▶ مثال: تنفيذ التكوين الديناميكي باستخدام توقيعات الفهرس
interface EnvConfig {
[key: string]: string | number | boolean;
// All values can be string、number or boolean
// Known properties must also be compatible.
NODE_ENV: string;
PORT: number;
}
let env: EnvConfig = {
NODE_ENV: "development",
PORT: 3000,
DEBUG: true, // ✅ Dynamic Properties
API_URL: "http://localhost:8080" // ✅ Dynamic Properties
};
console.log(`Environment:${env.NODE_ENV}`);
console.log(`Port:${env.PORT}`);
console.log(`Debugging:${env.DEBUG}`);
الناتج:
Environment:development
Port:3000
Debugging:true
5. أنواع الكائنات المتداخلة
في عملية التطوير الفعلية، غالبًا ما تكون الكائنات متداخلة على مستويات متعددة — ويدعم TypeScript وصف الأنواع طبقةً تلو الأخرى:
(1) أنواع القيم الثابتة المتداخلة
let user: {
name: string;
address: {
city: string;
zip: string;
};
tags: string[];
} = {
name: "Charlie",
address: {
city: "Beijing",
zip: "100000"
},
tags: ["Developer", "TypeScript"]
};
console.log(user.address.city); // "Beijing"
console.log(user.tags[0]); // "Developer"
(2) التداخل متعدد المستويات
type ApiResponse = {
status: number;
data: {
user: {
id: number;
profile: {
avatar: string;
bio: string;
};
};
};
};
let response: ApiResponse = {
status: 200,
data: {
user: {
id: 1,
profile: {
avatar: "https://example.com/avatar.png",
bio: "TypeScript Enthusiast"
}
}
}
};
console.log(response.data.user.profile.bio); // "TypeScript Enthusiast"
6. نمطان من أنواع الكائنات
(1) الأنواع المجهولة (المضمنة)
function greet(user: { name: string; age: number }): string {
return `Hello,${user.name}!`;
}
مناسب لـ: الأنواع البسيطة التي تُستخدم مرة واحدة فقط، ومعلمات الدوال
(2) أنواع التسمية (واجهة / نوع)
interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `Hello,${user.name}!`;
}
مناسب لـ: الاستخدامات المتعددة، والخصائص العديدة، والحالات التي تتطلب التوريث أو التوسيع
7. أنظمة الأنواع المنظمة
تستخدم TypeScript نظام أنواع منظم — فما دام هيكل الكائن يستوفي متطلبات النوع، فإنه يعتبر متوافقًا، بغض النظر عما إذا كانت أسماء الأنواع متطابقة أم لا:
(1) المطابقة الهيكلية
interface Point {
x: number;
y: number;
}
let point: Point = { x: 1, y: 2 }; // ✅ Has x and y
let point3D = { x: 1, y: 2, z: 3 };
point = point3D; // ✅ point3D has x and y,Redundant attributes do not conflict
(2) التحقق من وجود السمات غير الضرورية
عند تعيين قيمة مباشرةً إلى كائن حرفي، يقوم TypeScript بإجراء «فحص للخصائص الزائدة» — فهو لا يسمح بوجود خصائص غير موجودة في النوع المستهدف:
interface Point {
x: number;
y: number;
}
// ❌ Check for Unnecessary Attributes——No additional properties are allowed when directly assigning an object literal.
// let p: Point = { x: 1, y: 2, z: 3 };
// ✅ Method 1:Bypassing via Variables(Because variables do not trigger unnecessary property checks)
let temp = { x: 1, y: 2, z: 3 };
let p: Point = temp; // ✅
// ✅ Method 2:Using Type Assertions
let p2: Point = { x: 1, y: 2, z: 3 } as Point;
// ✅ Method 3:Extension Interface(Top Recommendations)
interface Point3D extends Point {
z: number;
}
let p3: Point3D = { x: 1, y: 2, z: 3 };
{ x: 1, y: 2, zzz: 3 }، فسوف ينبهك TypeScript على الفور — zzz ليست خاصية لـ Point؛ هل أخطأت في تهجئتها؟
▶ مثال: الأنواع المنظمة في الممارسة العملية
interface Printable {
toString(): string;
}
// Date has toString method,Structural Matching
let date: Printable = new Date();
// Custom objects include toString Methods,Also matches
let custom: Printable = {
toString() { return "Custom Objects"; }
};
function print(obj: Printable): void {
console.log(obj.toString());
}
print(date); // Output Date the string representation of
print(custom); // Output "Custom Objects"
الناتج:
// Executed successfully
❓ أسئلة شائعة
interface أم type؟interface أكثر ملاءمةً لتعريف أشكال الكائنات (حيث يدعم دمج الإعلانات والوراثة extends)، بينما يُعد type أكثر ملاءمةً للعمليات المعقدة مثل أنواع الاتحاد والأنواع الشرطية. طالما أن فريقك يتبع نهجًا متسقًا، فلا بأس بذلك. سترد مقارنة تفصيلية لاحقًا في هذا الفصل. يُنصح المبتدئون بالبدء باستخدام interface.undefined؟age?: number بغياب الخصائص (غيابها تمامًا)، بينما يشترط age: number | undefined وجود الخصائص، لكن قيمها يمكن أن تكون undefined. في التطوير الفعلي، تُستخدم الخصائص الاختيارية بشكل أكثر شيوعًا — حيث يبدو حذف خاصية ما أكثر طبيعية من كتابة undefined صراحةً.readonly غير قابلة للتغيير حقًّا؟readonly هي مجرد فحص يتم في مرحلة الترجمة؛ ولا توجد حماية في مرحلة التشغيل. ويمكن تجاوزها باستخدام تأكيدات الأنواع أو النوع any. ومع ذلك، فإنها تمنع بشكل فعال «التعديلات العرضية» — وهو الغرض الأساسي لنظام الأنواع: اكتشاف الأخطاء في وقت التحويل البرمجي، بدلاً من توفير الحماية في وقت التشغيل.📖 ملخص
- وصف نوع الكائن: "ما هي الخصائص التي يتمتع بها الكائن، وما هي أنواعها؟" استخدم صيغة
{ propertyName: type }. ?تشير إلى خاصية اختيارية؛readonlyتشير إلى خاصية للقراءة فقط؛ "readonly" تشير إلى خاصية للقراءة فقط على المستوى السطحي- توقيع الفهرس
[key: type]: valuetypeيصف كائنًا «يكون فيه نوع المفتاح غير محدد، بينما يكون نوع القيمة محددًا» - تصف الكائنات المتداخلة الأنواع طبقةً تلو الأخرى؛ وإذا كان هناك أكثر من 2–3 طبقات، يُنصح بفصلها إلى واجهات أو أنواع منفصلة.
- يستخدم TypeScript نظامًا للأنواع الهيكلي — فإذا تطابقت الهياكل، فإنها تكون متوافقة؛ ويتضمن التعيين المباشر لقيم الكائنات الفورية إجراء فحوصات للتأكد من عدم وجود خصائص زائدة عن الحاجة.
📝 تمارين
- المسألة الأساسية (الصعوبة ⭐): عرّف نوع كائن لتمثيل «كتاب» (العنوان، المؤلف، السعر، متوفر؟)، وأنشئ كائنين يمثلان كتابين — أحدهما متوفر والآخر غير متوفر — واطبع عناوينهما وأسعارهما.
- مشكلة متقدمة (درجة الصعوبة ⭐⭐): اكتب دالة
updateUser(user: { name: string; age: number; email?: string }, updates: { age?: number; email?: string })تُرجع كائن المستخدم المُحدَّث. يجب أن يكون كائن المستخدم الأصليnameغير قابل للتغيير (استخدمreadonly). - التحدي (الصعوبة ⭐⭐⭐): عرّف نوعًا
CacheStoreباستخدام توقيعات الفهرس، بحيث يكون المفتاح سلسلة نصية والقيمة هي{ data: T; timestamp: number }(حيث T هو نوع عام). أنشئ مثيلًا لـCacheStore<string>، وقم بتخزين إدخالين في ذاكرة التخزين المؤقتة فيه، ثم قم بالتكرار وعرض جميع المفاتيح المخزنة في ذاكرة التخزين المؤقتة وحالات انتهاء صلاحيتها (بافتراض أن مدة الصلاحية هي 5 ثوانٍ).