Menu
flag Ar iconالعربيةdown icon

Type Guard في TypeScript: كيف تفحص نوع كائن

الـ type guard فحص وقت التشغيل تفهمه TypeScript. تعرّف على الفحوص المدمجة، وكيف تكتب فحصك الخاص بـ type predicate بصيغة value is Type، وكيف تتحقق من أن كائنًا من نوع معين، ودوال التأكيد بـ asserts، والتحقق من البيانات من النوع unknown.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

الـ type guard فحص وقت التشغيل تفهمه TypeScript، فيضيّق النوع داخل الفرع الذي اجتاز الفحص. typeof وinstanceof وin فحوص مدمجة؛ ولأي شيء آخر تكتب دالة نوع قيمتها المُعادة type predicate، أي value is Type.

تعيد isUser قيمة منطقية عادية وقت التشغيل. يخبر نوع القيمة المُعادة value is User المترجم بما تثبته النتيجة true، فيضيّق كل if (isUser(x)) بعد ذلك x إلى User.

الـ Type Guards المدمجة

هذه الفحوص تضيّق دون أي دالة مساعدة:

الفحصمثالاستخدمه لـ
typeoftypeof x === "number"الأنواع البدائية والدوال
instanceofx instanceof Dateنسخ الأصناف
in"email" in xunions الكائنات، وخصائص كائنات unknown
Array.isArrayArray.isArray(x)المصفوفات
المساواةx === null، x.kind === "circle"null/undefined والوسوم الحرفية
القيمة المنطقيةif (x)حذف null وundefined

كلها تعمل JavaScript عادية. ما تضيفه TypeScript هو التضييق: تقرأ الفحص وتعدّل النوع في كل فرع. القائمة الكاملة للصيغ في صفحة تضييق الأنواع. الـ guard المخصص للفحوص التي لا تتسع لتعبير واحد، أو التي تريد إعادة استخدامها.

كتابة Type Predicate

صيغة الـ type predicate هي parameterName is Type، وتحل محل boolean في نوع القيمة المُعادة. يعمل التضييق في الاتجاهين: true تضيّق إلى Type، وfalse تحذف Type من الـ union.

تمرير guard إلى filter يعطي مصفوفة بنوع صحيح. ومنذ TypeScript 5.5 يستنتج المترجم أيضًا predicate من دوال الأسهم البسيطة، فيعيد pets.filter((p) => p.kind === "cat") النوع Cat[] دون guard مسمّى.

يجب أن يتوافق نوع الـ predicate مع نوع المعامل: function f(x: string): x is number هو الخطأ TS2677، A type predicate's type must be assignable to its parameter's type.

المترجم يثق بالـ Guard الذي تكتبه

تتحقق TypeScript من أن الـ guard يعيد قيمة منطقية، ولا تتحقق من صحة هذه القيمة. الـ guard الذي يعيد true لقيم خاطئة يجعل الأنواع كاذبة، فيفشل البرنامج وقت التشغيل دون أي خطأ ترجمة.

يرمي data.price.toFixed(2) الخطأ TypeError: Cannot read properties of undefined (reading 'toFixed') وقت التشغيل. قبل المترجم data.price على أنه number لأن الـ guard قال ذلك. افحص كل خاصية يعتمد عليها بقية الكود، واجعل الـ guards صغيرة ومختبرة وقريبة من النوع الذي تصفه.

التحقق من أن كائنًا من نوع معين

هذا هو السؤال وراء معظم الـ guards المخصصة: تصل البيانات بالنوع unknown (من JSON.parse أو fetch أو localStorage أو رسالة) وتحتاج أن تعرف هل تطابق الـ interface لديك. الطريقة:

  1. typeof value === "object" && value !== null (كائن، وليس null).
  2. "prop" in value لكل خاصية مطلوبة. على unknown يضيف in الخاصية إلى النوع بالنوع unknown.
  3. typeof value.prop === "..." (أو guard متداخل) لنوع كل خاصية.
  4. Array.isArray(value.items) && value.items.every(isItem) للمصفوفات.

مع الأشكال الكبيرة أو المتداخلة بعمق تصبح كتابة هذه الفحوص يدويًا مرهقة. مكتبات المخططات مثل Zod وValibot تتيح وصف الشكل مرة واحدة وتعطيك فحص وقت التشغيل ونوع TypeScript معًا.

دوال التأكيد: asserts value is Type

دالة التأكيد ترمي خطأ إذا فشل الفحص وتعود بشكل عادي في غير ذلك. نوع قيمتها المُعادة asserts value is Type (أو asserts condition)، ويُضيَّق كل ما بعد الاستدعاء دون حاجة إلى if.

قاعدة واحدة توقع الناس: يجب استدعاء دالة التأكيد عبر اسم له نوع صريح. دالة سهم const دون توصيف نوع، const check = (v: unknown): asserts v is string => {...}، تعطي الخطأ TS2775 في موضع الاستدعاء، Assertions require every name in the call target to be declared with an explicit type annotation. استخدم تعريف function، أو صِف الثابت بنوع دالة.

الـ Guards مقابل التأكيدات مقابل التحويل

الأداةفحص وقت التشغيل؟تضيّقعند الفشل
guard مدمج (typeof، in...)نعمداخل الفرعيُنفَّذ الفرع الآخر
دالة value is Tنعم (بكودك)داخل الفرعيُنفَّذ الفرع الآخر
دالة asserts value is Tنعم (بكودك)بعد الاستدعاءترمي خطأ
value as Tلاالتعبيرلا شيء: النوع الخاطئ ينتشر

تأكيد النوع (as) يغيّر النوع دون أن يفحص شيئًا. عند حدود البرنامج حيث تأتي البيانات من الخارج، الـ guard أو دالة التأكيد هما النسخة الآمنة من الفكرة نفسها.

Guards مبنية على this في الأصناف

يستطيع التابع تضييق الكائن الذي يُستدعى عليه باستخدام this is Type. هذا مفيد في هرميات الأصناف:

class FileNode {
  constructor(public name: string) {}
  isDirectory(): this is DirectoryNode {
    return this instanceof DirectoryNode;
  }
}

class DirectoryNode extends FileNode {
  children: FileNode[] = [];
}

function count(node: FileNode): number {
  return node.isDirectory() ? node.children.length : 0; // node: DirectoryNode in the true branch
}

الأسئلة الشائعة

ما هو الـ type guard في TypeScript؟

أي فحص وقت التشغيل تستخدمه TypeScript لتضييق النوع: typeof x === "string"، وx instanceof Date، و"id" in x، وArray.isArray(x)، أو استدعاء دالة نوع قيمتها المُعادة type predicate مثل x is User. داخل الفرع الذي اجتاز الفحص يأخذ المتغير النوع الأضيق.

كيف أتحقق من أن كائنًا من نوع معين في TypeScript؟

الأنواع غير موجودة وقت التشغيل، لذلك تفحص الخصائص: اكتب دالة isUser(value: unknown): value is User تختبر typeof value === "object" وvalue !== null وكل خاصية مطلوبة باستخدام in وtypeof. بعد if (isUser(x)) يصبح نوع x هو User. وللأصناف يكفي x instanceof MyClass.

ماذا تعني "value is Type" في TypeScript؟

إنها type predicate تُستخدم نوعًا للقيمة المُعادة من دالة. تظل الدالة تعيد قيمة منطقية وقت التشغيل، لكن عندما تعيد true تضيّق TypeScript الوسيط إلى Type في موضع الاستدعاء، وعندما تعيد false تضيّقه إلى بقية أعضاء الـ union. لا يتحقق المترجم من جسم الدالة، فيجب أن يكون الفحص صحيحًا.

ما الفرق بين الـ type guard ودالة التأكيد؟

الـ type guard (x is T) يعيد قيمة منطقية ويضيّق داخل if. أما دالة التأكيد (asserts x is T) فلا تعيد شيئًا وترمي خطأ عند فشل الفحص، فيُضيَّق كل ما بعد الاستدعاء دون if. استخدم الـ guards للتفرع، والتأكيدات لحالة «يجب أن يتحقق هذا، وإلا توقف».

هل يمكنني التحقق من أن كائنًا يطبّق interface في TypeScript؟

ليس مباشرة: الـ interfaces تُحذف عند الترجمة وinstanceof لا يقبلها. اكتب type guard يفحص خصائص الـ interface، أو أضف خاصية وسم حرفية (kind: "user") وقارن بها. مكتبات المخططات مثل Zod تولّد الفحص والنوع معًا من تعريف واحد.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن