أنواع الأدوات (utility types) أنواع generic مدمجة في TypeScript تحوّل نوعًا إلى نوع آخر. بدلًا من كتابة نوع User ثانٍ كل حقوله اختيارية، تكتب Partial<User>؛ وبدلًا من نسخ ثلاثة حقول، تكتب Pick<User, "id" | "name">. إنها عامة (global)، فلا حاجة إلى استيرادها.
عندما يتغير User تتبعه الأنواع المشتقة الأربعة كلها. تعرّف المكتبة القياسية في TypeScript (lib.es5.d.ts) اثنين وعشرين نوع أداة. تسردها الأقسام التالية جميعًا، مجمّعة حسب نوع الأنواع التي تعمل عليها، مع رابط إلى الصفحة المفصلة حيث توجد.
أنواع الكائنات: Partial وRequired وReadonly وPick وOmit وRecord
| نوع الأداة | ماذا يفعل | مثال |
|---|---|---|
Partial<T> | يجعل كل خاصية اختيارية | Partial<User> لبيانات التحديث |
Required<T> | يجعل كل خاصية إلزامية (يزيل ?) | Required<Config> بعد تطبيق القيم الافتراضية |
Readonly<T> | يجعل كل خاصية readonly | Readonly<State> |
Pick<T, K> | يُبقي المفاتيح K فقط | Pick<User, "id" | "name"> |
Omit<T, K> | يحذف المفاتيح K | Omit<User, "password"> |
Record<K, V> | نوع كائن مفاتيحه K وقيمه V | Record<"en" | "de", string> |
Required<T> هو عكس Partial<T>؛ تشرح صفحة Partial الاثنين، بما في ذلك قيمة undefined الصريحة التي يسمح بمرورها نشر (spread) كهذا.
أنواع union: Exclude وExtract وNonNullable
| نوع الأداة | ماذا يفعل | مثال |
|---|---|---|
Exclude<U, M> | يحذف أعضاء union القابلة للإسناد إلى M | Exclude<"a" | "b" | "c", "a"> هو "b" | "c" |
Extract<U, M> | يُبقي أعضاء union القابلة للإسناد إلى M | Extract<string | number, number> هو number |
NonNullable<T> | يحذف null وundefined | NonNullable<string | null> هو string |
تعمل هذه الثلاثة على unions لا على الكائنات. وهذا هو الفرق الأساسي بينها وبين Pick وOmit، اللذين يأخذان نوع كائن وقائمة من مفاتيحه.
أنواع الدوال والأصناف: Parameters وReturnType وغيرهما
| نوع الأداة | ماذا يفعل | مثال |
|---|---|---|
ReturnType<F> | نوع القيمة المُعادة من نوع دالة | ReturnType<typeof createStore> |
Parameters<F> | أنواع المعاملات على شكل tuple | Parameters<typeof fetchPage>[0] |
ConstructorParameters<C> | معاملات مُنشئ الصنف على شكل tuple | ConstructorParameters<typeof Point> |
InstanceType<C> | نوع النسخة التي ينشئها المُنشئ | InstanceType<typeof Point> |
ThisParameterType<F> | نوع المعامل this في دالة | ThisParameterType<typeof greet> |
OmitThisParameter<F> | نوع الدالة دون المعامل this | نوع greet.bind(obj) |
ThisType<T> | يحدد نوع this داخل دوال كائن حرفي | يُستخدم مع noImplicitThis في واجهات builder |
NoInfer<T> | يمنع استنتاج معامل النوع من هذا الموضع | fallback: NoInfer<C> |
نحتاج إلى typeof createOrder لأن هذه الأدوات تأخذ نوعًا، وcreateOrder قيمة. والأمر نفسه مع الأصناف: typeof Point هو نوع المُنشئ، بينما Point وحده كنوع يعني نوع النسخة.
يتحكم NoInfer في المصدر الذي يأخذ منه النوع العام نوعه:
بدون NoInfer تستنتج TypeScript النوع C من الوسيطين معًا وتوسّعه إلى "red" | "green" | "blue"، فيُقبل الخطأ الإملائي في القيمة الاحتياطية.
أنواع النصوص: Uppercase وLowercase وCapitalize وUncapitalize
| نوع الأداة | ماذا يفعل | مثال |
|---|---|---|
Uppercase<S> | يحوّل نوعًا حرفيًا نصيًا إلى أحرف كبيرة | Uppercase<"get"> هو "GET" |
Lowercase<S> | يحوّله إلى أحرف صغيرة | Lowercase<"GET"> هو "get" |
Capitalize<S> | يكبّر الحرف الأول | Capitalize<"name"> هو "Name" |
Uncapitalize<S> | يصغّر الحرف الأول | Uncapitalize<"Name"> هو "name" |
هذه الأربعة مدمجة في المترجم بدلًا من أن تكون مكتوبة بـ TypeScript، وأكثر ما تفيد داخل أنواع template literal، مثل `on${Capitalize<E>}` لأسماء معالجات الأحداث.
الوعود: Awaited
| نوع الأداة | ماذا يفعل | مثال |
|---|---|---|
Awaited<T> | النوع الذي تحصل عليه من await، مع فك الوعود المتداخلة | Awaited<Promise<Promise<number>>> هو number |
Awaited<ReturnType<typeof fn>> هو الطريقة المعتادة لتسمية نوع نتيجة دالة غير متزامنة دون تعريفه منفصلًا.
الجمع بين utility types
تتداخل أنواع الأدوات. وبعض التركيبات تتكرر بما يكفي لتستحق الحفظ:
اقرأ نوع الأداة المتداخل من الداخل إلى الخارج: Readonly<Pick<Post, "id" | "title">> يُبقي أولًا خاصيتين، ثم يجعلهما للقراءة فقط. والأجزاء نفسها تبني مساعدًا PartialBy يجعل بعض المفاتيح فقط اختيارية؛ وتكتبه صفحة Partial كاملًا.
utility types لا تفعل شيئًا وقت التشغيل
كل نوع أداة يُحذف عند ترجمة الكود. القيمة من النوع Omit<User, "password"> قد تحمل كلمة مرور وقت التشغيل إذا كان الكائن الذي جاءت منه يحملها:
النوع يحدّ فقط مما يُسمح لكودك بقراءته. لحذف حقل من البيانات، أخرجه بالتفكيك كما في الأسطر الأخيرة، ولمنع التعديل وقت التشغيل استخدم Object.freeze لا Readonly. الأنواع المدمجة mapped types وconditional types من سطر واحد، فالأدوات نفسها تتيح لك كتابة أنواعك الخاصة.
الأسئلة الشائعة
ما هي utility types في TypeScript؟
أنواع generic تأتي مع TypeScript وتحوّل أنواعًا أخرى: Partial<T> يجعل كل خاصية اختيارية، وPick<T, K> يُبقي بعض الخصائص، وReturnType<F> يعطي نوع القيمة المُعادة من دالة، وهكذا. إنها معرّفة في المكتبة القياسية، فتستخدمها دون استيراد أي شيء.
هل أحتاج إلى استيراد utility types؟
لا. Partial وOmit وRecord وReturnType وبقيتها أنواع عامة (global) من ملفات المكتبة المدمجة في TypeScript. اكتب Partial<User> في أي مكان؛ لا حاجة إلى import ولا إلى حزمة npm.
ما هي utility types المدمجة في TypeScript؟
22 نوعًا، كلها معرّفة في lib.es5.d.ts: Partial وRequired وReadonly وPick وOmit وRecord وExclude وExtract وNonNullable وParameters وConstructorParameters وReturnType وInstanceType وThisParameterType وOmitThisParameter وThisType وNoInfer وAwaited وUppercase وLowercase وCapitalize وUncapitalize.
هل تغيّر utility types الكائنات وقت التشغيل؟
لا. إنها تصف الأنواع فقط وتُحذف من مخرجات JavaScript. Omit<User, "password"> لا يحذف خاصية password، وReadonly<T> لا يجمّد شيئًا. لتغيير الكائن الفعلي اكتب الكود: نمط rest في التفكيك، أو Object.freeze، وهكذا.
هل يمكنني كتابة utility types خاصة بي؟
نعم. الأنواع المدمجة مكتوبة بـ TypeScript عادية: معظمها mapped types أو conditional types من سطر واحد في lib.es5.d.ts. والنوع type Nullable<T> = { [K in keyof T]: T[K] | null } نوع أداة مخصص مكتوب بالطريقة نفسها.