TypeScript: متغيرات TypeScript واستنتاج الأنواع

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

تعد «استنتاج الأنواع» إحدى أقوى ميزات TypeScript — فأنت لست بحاجة إلى كتابة تعليقات الأنواع في كل مكان؛ حيث يقوم المُجمِّع باستنتاجها تلقائيًّا نيابةً عنك. ومع ذلك، عليك فهم قواعد الاستنتاج حتى تتمكن من كتابة كود يمكن للمُجمِّع تفسيره بشكل صحيح.

1. إعلان المتغيرات: let وconst وvar

يدعم TypeScript بشكل كامل الطرق الثلاث لإعلان المتغيرات في JavaScript، ولكن يُنصح باستخدام let وconst:

(1) let — متغير يمكن إعادة تعيين قيمته

TYPESCRIPT
let count = 0;
count = 1;        // ✅ let Variables can be reassigned

(2) const — ثابت لا يمكن إعادة تعيين قيمته

TYPESCRIPT
const MAX_SIZE = 100;
MAX_SIZE = 200;   // ❌ Error:Cannot be assigned to a constant

(3) var — نطاق الدالة (غير موصى به)

TYPESCRIPT
var x = 1;        // ⚠️ Available but not recommended——var There are issues with variable hoisting and scope leakage.
📌 نقطة أساسية: استخدم دائمًا let وconst؛ ولا تستخدم var. إذا كان بإمكانك استخدام const، فاستخدم const بدلاً منه (عندما تظل القيمة دون تغيير)؛ ولا تستخدم let إلا عندما تحتاج إلى إعادة تعيين القيمة. وهذا يتوافق تمامًا مع أفضل ممارسات JavaScript.

(4) التعليقات التوضيحية للأنواع مقابل استدلال الأنواع

TYPESCRIPT
// Type Annotations——Tell me TypeScript What is the type of the variable?
let age: number = 25;

// Type Inference——TypeScript Automatically determine the type based on the initial value
let name = "Charlie";    // TypeScript Inferred as string
let active = true;    // TypeScript Inferred as boolean

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



2. القواعد الكاملة لاستدلال الأنواع

استنتاج الأنواع ليس «تخمينًا» — بل يتبع قواعد واضحة. ولا يمكنك التنبؤ بسلوك المُجمِّع إلا من خلال فهم هذه القواعد.

(1) القاعدة 1: استنتاج القيم الأولية

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

TYPESCRIPT
let message = "hello";   // Inferred as string
let count = 42;          // Inferred as number
let flag = true;         // Inferred as boolean
let items = [1, 2, 3];   // Inferred as number[]

(2) القاعدة 2: استدلال النوع للقيم الثابتة const

هذه واحدة من أكثر التفاصيل دقة في استدلال الأنواع في TypeScript — حيث تختلف نتائج الاستدلال لـ let وconst:

TYPESCRIPT
// let Inferred as a wide type(widening)
let x = "hello";     // Inferred as string
let n = 42;          // Inferred as number
let b = true;        // Inferred as boolean

// const Inferred as an exact literal type(no widening)
const x2 = "hello";  // Inferred as "hello"(Literal types,No string)
const n2 = 42;       // Inferred as 42(Literal types,No number)
const b2 = true;     // Inferred as true(Literal types,No boolean)
💡 لماذا تم تصميمه بهذه الطريقة؟ let يمكن إعادة تعيين المتغير، لذا يُستنتج أن له نوعًا أوسع (يمكن أن يحتوي string على أي سلسلة أحرف)؛ const يبقى المتغير دون تغيير، لذا يُستنتج أن له النوع الحرفي المحدد. يُسمى هذا السلوك التوسيع.

(3) القاعدة 3: إذا لم يتم تحديد قيمة أولية، يتم استنتاجها على أنها any

إذا تم تعريف متغير دون قيمة أولية أو تعليق نوع، فإن TypeScript يستنتج أنه any:

TYPESCRIPT
let something;        // Inferred as any(Danger!)
something = 42;       // any,Do not check
something = "hello";  // any,Do not check——Completely bypassed the type system
🔥 خطأ شائع: يعمل any فعليًّا على تعطيل التحقق من الأنواع، مما يجعله لا يختلف عن كتابة لغة جافا سكريبت. وعند تمكين وضع strict، ستؤدي المتغيرات التي لا تحتوي على قيم أولية إلى ظهور خطأ (يجب عليك إما تحديد النوع صراحةً أو توفير قيمة أولية).

(4) القاعدة 4: أفضل استدلال للأنواع العامة

عند استنتاج الأنواع من مصادر متعددة، يبحث TypeScript عن «أفضل نوع مشترك» بينها:

TYPESCRIPT
let arr = [1, "hello", true];
// Inferred as (number | string | boolean)[] —— Union Type Arrays

let arr2 = [1, 2, 3];
// Inferred as number[] —— All elements are of the same type,Inferred as an array of element types

▶ مثال: تجربة استدلال الأنواع

TYPESCRIPT
// Experiment1:let vs const Differences in Inference
let dynamicText = "hello";
const fixedText = "hello";

dynamicText = "world";     // ✅ string Can accept another string
// fixedText = "world";    // ❌ const Cannot be reassigned

// Experiment2:Inference of Object Properties
let user = {
  name: "Charlie",
  age: 20
};
// Inferred as { name: string; age: number }
user.name = "Diana";        // ✅ string Properties can be assigned new values
user.age = 21;             // ✅ number Properties can be assigned new values

// Experiment3:const Object——The property can still be modified!
const config = {
  host: "localhost",
  port: 3000
};
config.host = "127.0.0.1";  // ✅ const Only reassigning the object itself is prohibited.,Non-freezing property
// config = { host: "x", port: 1 };  // ❌ You cannot reassign the entire object
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
// Executed successfully


3. أساسيات توافق الأنواع

يعتمد التوافق بين الأنواع في TypeScript على «التصنيف الفرعي الهيكلي» — أي أن الأنواع تكون متوافقة طالما أن هياكلها متطابقة، دون الحاجة إلى أن تكون أسماء الأنواع متطابقة.

(1) توافق الأنواع الفرعية

TYPESCRIPT
let num: number = 42;
let big: bigint = 100n;

// num = big;    // ❌ bigint No number subtypes of
// big = num;    // ❌ number No bigint subtypes of

(2) الأنواع الحرفية هي أنواع فرعية من الأنواع الواسعة

TYPESCRIPT
let text: string = "hello";    // string Type
const literal: "hello" = "hello";  // Literal types "hello"

text = literal;   // ✅ "hello" is a string subtype of,Can be assigned a value
// literal = text; // ❌ string No "hello" subtypes of
📌 تشبيه: العلاقة بين الأنواع الحرفية والأنواع الواسعة تشبه العلاقة بين «التفاحة» و«الفاكهة» — فالتفاحة نوع من الفاكهة، لكن الفاكهة ليست بالضرورة تفاحة.

(3) تشير كلمة «أي» إلى «كبسولة الهروب»

TYPESCRIPT
let a: any = "hello";
a = 42;          // ✅ any Can accept any type
a = true;        // ✅ any No tests will be performed

let b: string = a;  // ✅ any It can also be assigned to any type.(Danger!)
⚠️ تحذير: any ينتهك أمان الأنواع ولا ينبغي استخدامه إلا في الحالات التالية: (1) النماذج الأولية السريعة، (2) ترحيل كود جافا سكريبت القديم، أو (3) كحل مؤقت عندما تفتقر مكتبة تابعة لجهة خارجية إلى تعريفات الأنواع. ينبغي تجنب استخدام any في عمليات التطوير العادية.



4. الجمع بين تأكيدات الأنواع واستدلال الأنواع

إذا كانت نتيجة الاستدلال غير مرغوبة، فيمكنك تجاوزها باستخدام تأكيد النوع:

TYPESCRIPT
// Scene:DOM Type Inference for Elements
let input = document.getElementById("myInput");
// Inferred as HTMLElement | null——I don't know exactly what element it is.

let inputEl = document.getElementById("myInput") as HTMLInputElement;
// Assert that HTMLInputElement——Tell the compiler"I'm sure this is input Element"
💡 نصيحة: لا تُجري تأكيدات الأنواع تحويلات أثناء وقت التشغيل؛ فهي مجرد تعليقات توضيحية للأنواع في وقت التحويل البرمجي. وإذا كان التأكيد غير صحيح، فستظل المشكلات تحدث أثناء وقت التشغيل. وسيتم شرح ذلك بالتفصيل في الدرس 18.



5. استراتيجيات الاستدلال في عملية التطوير الفعلي

(1) الاستراتيجية 1: إذا كان بإمكانك استنتاج الأمر، فلا تدونه

TYPESCRIPT
// ❌ Redundancy
let name: string = "Charlie";
const MAX: number = 100;
function add(a: number, b: number): number { return a + b; }

// ✅ Concise
let name = "Charlie";
const MAX = 100;
function add(a: number, b: number) { return a + b; }  // The return type is inferred as number

(2) الإستراتيجية 2: يجب تحديد الواجهات وأسماء النوع البديلة

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

// When an object literal is assigned to a typed variable,Inference+Type checking takes effect simultaneously
let user: User = { name: "Charlie", age: 20 };  // ✅ Structural Matching
let bad: User = { name: "Charlie" };             // ❌ Missing age Properties

(3) الاستراتيجية 3: وضع علامات صريحة على التعبيرات المعقدة

TYPESCRIPT
// Return Values of Complex Functions——It is recommended to explicitly label them.,Avoiding Fallacies in Reasoning
function getUserInfo(id: number): { name: string; age: number } | null {
  if (id <= 0) return null;
  return { name: "User" + id, age: 20 };
}

▶ مثال: الاستدلال في الممارسة العملية — كائنات التكوين

TYPESCRIPT
// Use as const assertion to make object properties read-only literal types
const THEME = {
  primary: "#4A90D9",
  secondary: "#7B51D9",
  fontSize: 14
} as const;

// Inference Results:
// { readonly primary: "#4A90D9"; readonly secondary: "#7B51D9"; readonly fontSize: 14 }
// Each property is of an exact literal type + readonly

// THEME.primary = "#000";  // ❌ readonly Properties cannot be modified
// THEME.fontSize = 16;     // ❌ readonly Properties cannot be modified

console.log(THEME.primary);   // "#4A90D9"
console.log(THEME.fontSize);  // 14
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
#4A90D9
14
💡 الغرض من as const: يوجه هذا المُعدِّل لغة TypeScript إلى استنتاج النوع الحرفي الأكثر دقة للقيمة (دون توسيع النطاق) وتطبيق المُعدِّل readonly. ويُستخدم عادةً في حالات مثل تعريف التكوينات الثابتة وأنواع الإجراءات.


▶ مثال: استنتاج الأنواع مع المصفوفات والكائنات

TYPESCRIPT
// Array inference — element type is inferred automatically
let scores = [90, 85, 92];           // number[]
let names = ["Alice", "Bob"];        // string[]
let mixed = [1, "two", true];       // (number | string | boolean)[]

// Object inference — each property gets its own type
let point = { x: 10, y: 20 };       // { x: number; y: number }
let user = { name: "Eve", active: true }; // { name: string; active: boolean }

// Nested inference
let config = {
  server: { host: "localhost", port: 3000 },
  debug: true
};
// { server: { host: string; port: number }; debug: boolean }

console.log(config.server.host);    // "localhost"
console.log(typeof mixed[0]);       // "number"
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
localhost
number


❓ أسئلة شائعة

س متى يكون من الضروري تحديد تعليقات الأنواع يدويًّا؟
ج في ثلاث حالات: (1) معلمات الدوال — عندما لا توجد قيمة أولية لاستنتاج النوع؛ (2) إعلانات المتغيرات التي لا تحتوي على قيم أولية — وإلا، يُستنتج النوع على أنه any؛ (3) أنواع القيم المرجعة التي لا تتطابق مع التوقعات — عندما يكون النوع المستنتج واسعًا جدًّا أو ضيقًا جدًّا، يجب تجاوزه.
س ما هي العلاقة بين any و TypeScript؟ هل يُنصح باستخدام any؟
ج any هي «نافذة الهروب من الأنواع» في TypeScript — فهي تعطل التحقق من الأنواع، مما يؤدي فعليًّا إلى العودة إلى وضع JavaScript. يجب ألا تستخدم any إلا إذا كنت تقوم بترحيل كود قديم أو تتعامل مؤقتًا مع مشكلة متعلقة بالأنواع. إذا كنت غير متأكد من نوع ما، فإن استخدام unknown (الموضح بالتفصيل في الدرس 5) أكثر أمانًا من استخدام any.
س ما هو as const؟ وكيف يختلف عن const العادي؟
ج يمنع const العادي فقط إعادة تعيين المتغير؛ بينما لا يزال استدلال النوع يسمح بالتوسيع (على سبيل المثال، يُستدل على const x = "hi" على أنه "hi"، لكن خصائص الكائنات تظل من النوع الأوسع). as const هو تأكيد نوع يؤدي إلى استنتاج جميع المستويات على أنها قيم ثابتة بالإضافة إلى readonly. ولا يمكنك تحقيق سلوك «قراءة فقط» عميق حقيقي إلا باستخدام as const مع الكائنات أو المصفوفات.

📖 ملخص

📝 تمارين

  1. تمرين أساسي (مستوى الصعوبة ⭐): قم بتعريف متغير سلسلة باستخدام let ومتغير آخر باستخدام const، ثم اطبع قيمهما. بعد ذلك، حاول إعادة تعيين قيمهما ولاحظ كيف يتعامل TypeScript مع الخطأ.
  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): قم بتعريف كائن تكوين as const باسم COLORS يحتوي على ثلاث قيم ألوان سداسية عشرية: الأحمر والأخضر والأزرق. ثم حاول تعديل أحد الألوان ولاحظ رسالة الخطأ التي يولدها القيد readonly.
  3. التحدي (الصعوبة: ⭐⭐⭐): اكتب دالة createUser(name: string, age: number) دون تحديد نوع القيمة المرجعة، بحيث يُسمح لـ TypeScript باستنتاج نوع القيمة المرجعة. بعد ذلك، استخدم متغيرًا محدد النوع خارج الدالة لاستلام القيمة المرجعة والتحقق من صحة الاستنتاج.
Web-Tutorial.com

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

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

100%