فحوصات الأنواع الشرطية في TypeScript: دليل شامل مع أمثلة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مكتبة conditional-type-checks لـ TypeScript: اختبار الأنواع

TypeScript هي لغة قوية بنظام أنواع معقد يشمل الأنواع الشرطية، والتخطيط، واستنتاج الأنواع، وغيرها الكثير. لكن المطورين غالبًا ما يواجهون مشكلة: كيف نتحقق من أن الأنواع تعمل بالضبط كما هو مطلوب؟ اختبارات الوحدة التقليدية تتحقق من القيم أثناء التشغيل، لكنها لا تستطيع ضمان صحة الأنواع في مرحلة التجميع. هنا يأتي دور مكتبة conditional-type-checks.

توفر هذه المكتبة مجموعة من الأدوات لاختبار الأنواع بشكل ثابت في TypeScript. تتيح لك كتابة اختبارات تتحقق من أن نوعًا معينًا يطابق التوقعات: هل هو نوع فرعي من نوع آخر؟ هل النوعان متساويان؟ هل يمكن تعيين قيمة لنوع آخر؟ وهكذا. يتم إجراء جميع الفحوصات في مرحلة التجميع دون التأثير على وقت التشغيل.

لماذا تحتاج هذا؟ أولاً، لإعادة هيكلة الأنواع المعقدة — يمكنك التأكد من أن التغييرات لن تكسر السلوك المتوقع. ثانيًا، لتوثيق النوايا — اختبارات الأنواع تعمل كمواصفات حية. ثالثًا، للمكتبات التي توفر أنواعًا معقدة — سيكون المستخدمون واثقين من صحتها. المكتبة مفيدة بشكل خاص عند العمل مع الأنواع الشرطية (extends, infer)، والقوالب الحرفية، والأنواع العودية.

التثبيت

يتم تثبيت المكتبة عبر npm كاعتماد للتطوير:

npm install --save-dev conditional-type-checks

أو عبر yarn:

yarn add --dev conditional-type-checks

لا حاجة لأي إعدادات إضافية في TypeScript — المكتبة تتكون بالكامل من أنواع ولا تُنتج كود JavaScript.

البداية السريعة

أنشئ ملفًا باسم type-tests.ts واكتب اختبارًا بسيطًا:

import { IsExact } from 'conditional-type-checks';

type TestType = string;
type ExpectedType = string;

// التحقق من تطابق الأنواع تمامًا
type TestPass = IsExact<TestType, ExpectedType>; // true

// إذا لم تتطابق الأنواع، سيحدث خطأ في التجميع
// type TestFail = IsExact<TestType, number>; // false

لجعل الاختبار يتحقق فعليًا من شيء ما، استخدم بناء expectType أو مجرد تعيين مع التحقق:

import { assert } from 'conditional-type-checks';

type MyType = { name: string; age: number };

// التحقق من أن MyType هو كائن يحتوي على الحقلين name و age
type Check = assert<IsExact<MyType, { name: string; age: number }>>; // OK

إذا فشل التحقق، سيعرض TypeScript خطأ تجميع مع رسالة Type 'false' does not satisfy the constraint 'true'.

الدوال والطرق الرئيسية

توفر المكتبة عدة أدوات رئيسية. دعنا نستعرض كل منها بالتفصيل.

1. IsExact<T, U>

التوقيع: type IsExact<T, U> = ...

الوظيفة: تتحقق مما إذا كان النوعان T و U متطابقين تمامًا. تُرجع true إذا كان النوعان متطابقين، وإلا تُرجع false. تراعي جميع تفاصيل TypeScript بما في ذلك readonly و optional والمعدلات الأخرى.

import { IsExact } from 'conditional-type-checks';

type A = { readonly x: number };
type B = { x: number };

type Test1 = IsExact<A, B>; // false — readonly مختلف
type Test2 = IsExact<string, string>; // true
type Test3 = IsExact<string | number, number | string>; // true — الترتيب غير مهم
type Test4 = IsExact<{ a: string }, { a: string; b?: number }>; // false

2. IsAny<T>

التوقيع: type IsAny<T> = ...

الوظيفة: تتحقق مما إذا كان النوع T هو من النوع any. تُرجع true إذا كان any، وإلا تُرجع false. مفيدة جدًا لاكتشاف الاستخدام غير المقصود لـ any في قاعدة الكود.

import { IsAny } from 'conditional-type-checks';

type Test1 = IsAny<any>; // true
type Test2 = IsAny<string>; // false
type Test3 = IsAny<unknown>; // false
type Test4 = IsAny<never>; // false

3. IsNever<T>

التوقيع: type IsNever<T> = ...

الوظيفة: تتحقق مما إذا كان النوع T هو never. تُرجع true إذا كان never، وإلا تُرجع false. مفيدة لاختبار الأنواع المستبعدة أو الفارغة.

import { IsNever } from 'conditional-type-checks';

type Test1 = IsNever<never>; // true
type Test2 = IsNever<string>; // false
type Test3 = IsNever<string | never>; // false — لأن | never يُبسط إلى string
type Test4 = IsNever<string & number>; // true — التقاطع فارغ

4. IsUnknown<T>

التوقيع: type IsUnknown<T> = ...

الوظيفة: تتحقق مما إذا كان النوع T هو unknown. تُرجع true إذا كان unknown، وإلا تُرجع false. مفيدة للتمييز بين unknown والأنواع الأخرى.

import { IsUnknown } from 'conditional-type-checks';

type Test1 = IsUnknown<unknown>; // true
type Test2 = IsUnknown<any>; // false
type Test3 = IsUnknown<string>; // false
type Test4 = IsUnknown<never>; // false

5. IsUnion<T>

التوقيع: type IsUnion<T> = ...

الوظيفة: تتحقق مما إذا كان النوع T هو نوع اتحادي (union). تُرجع true إذا كان اتحادًا، وإلا تُرجع false. مفيدة لاختبار ما إذا كان النوع يمثل أكثر من قيمة محتملة.

import { IsUnion } from 'conditional-type-checks';

type Test1 = IsUnion<string | number>; // true
type Test2 = IsUnion<string>; // false
type Test3 = IsUnion<never>; // false
type Test4 = IsUnion<'a' | 'b' | 'c'>; // true

6. Has<T, U>

التوقيع: type Has<T, U> = ...

الوظيفة: تتحقق مما إذا كان النوع T يحتوي على النوع U (أي أن U يمتد T). تُرجع true إذا كان U نوعًا فرعيًا من T، وإلا تُرجع false. هذه هي الطريقة العكسية لـ extends.

import { Has } from 'conditional-type-checks';

type Test1 = Has<string, 'hello'>; // true — 'hello' يمتد string
type Test2 = Has<number, 42>; // true — 42 يمتد number
type Test3 = Has<string, number>; // false
type Test4 = Has<{ name: string }, { name: string; age: number }>; // true

7. AssertTrue<T> و AssertFalse<T>

التوقيع: type AssertTrue<T extends true> = ... و type AssertFalse<T extends false> = ...

الوظيفة: تُستخدم لتأكيد أن النوع T هو true أو false على التوالي. إذا لم يكن الأمر كذلك، يحدث خطأ في التجميع. هذه هي الطريقة الموصى بها لإنشاء اختبارات الأنواع.

import { AssertTrue, AssertFalse, IsExact } from 'conditional-type-checks';

type MyType = { x: number; y: string };

// اختبارات ناجحة
type Test1 = AssertTrue<IsExact<MyType, { x: number; y: string }>>;
type Test2 = AssertFalse<IsExact<MyType, { x: number }>>;

// إذا فشل الاختبار، سيظهر خطأ تجميع
// type Test3 = AssertTrue<IsExact<MyType, { x: number }>>; // خطأ!

أمثلة عملية متقدمة

دعنا نرى بعض الأمثلة الواقعية لاستخدام المكتبة في مشاريع TypeScript.

اختبار الأنواع الشرطية

import { AssertTrue, IsExact } from 'conditional-type-checks';

// نوع شرطي يستخرج نوع العنصر من مصفوفة
type ElementType<T> = T extends (infer U)[] ? U : never;

// اختبارات
type Test1 = AssertTrue<IsExact<ElementType<string[]>, string>>;
type Test2 = AssertTrue<IsExact<ElementType<number[][]>, number[]>>;
type Test3 = AssertTrue<IsExact<ElementType<boolean>, never>>;

اختبار الأنواع المعقدة من API

import { AssertTrue, IsExact } from 'conditional-type-checks';

// لنفترض أن لدينا نوعًا من استجابة API
type ApiResponse<T> = {
  data: T;
  status: 'success' | 'error';
  message?: string;
};

// اختبار أن ApiResponse يعمل بشكل صحيح
type Test1 = AssertTrue<IsExact<
  ApiResponse<{ id: number; name: string }>,
  {
    data: { id: number; name: string };
    status: 'success' | 'error';
    message?: string;
  }
>>;

الخلاصة

مكتبة conditional-type-checks هي أداة لا غنى عنها لأي مطور TypeScript يريد ضمان صحة أنواعه. من خلال دمج اختبارات الأنواع في سير العمل الخاص بك، يمكنك اكتشاف الأخطاء في مرحلة التجميع بدلاً من وقت التشغيل، مما يوفر الوقت والجهد. سواء كنت تعمل على مكتبة عامة أو مشروع خاص، فإن هذه المكتبة ستساعدك في كتابة كود TypeScript أكثر أمانًا وموثوقية.

ابدأ باستخدامها اليوم وأضف طبقة إضافية من الأمان إلى مشاريعك!

توصيات