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

Omit في TypeScript: حذف خصائص من نوع (أمثلة)

Omit<T, K> ينشئ نوعًا فيه كل خصائص T عدا المفاتيح K. تعرّف على Omit بمفتاح واحد وبعدة مفاتيح، واستبدال نوع خاصية، وحذف الخاصية وقت التشغيل، والفرق بين Omit وExclude وPick، ولماذا يقبل Omit مفاتيح غير موجودة، ونسخة Omit صارمة، وOmit على أنواع union.

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

Omit<T, K> نوع أداة مدمج ينشئ نوع كائن فيه كل خصائص T عدا المفاتيح المذكورة في K. الاستخدام الكلاسيكي: نوع User فيه password، ونسخة عامة ليس فيها.

يبقى PublicUser متزامنًا مع User: أضف حقلًا إلى User فيظهر في PublicUser أيضًا، بينما يبقى password خارجه. يعمل Omit على الأنواع فقط؛ أما التفكيك في toPublic فهو ما يحذف الخاصية من الكائن الفعلي.

حذف عدة مفاتيح

الوسيط الثاني union، فاذكر ما تحتاج من المفاتيح مفصولة بـ |:

«النوع دون الحقول التي يولّدها الخادم» هو أشيع استخدام لـ Omit في الكود الحقيقي: نوع نموذج واحد، ويشتق Omit منه شكل المُدخلات لنماذج الإنشاء وطلبات API.

كيف يُعرَّف Omit

يُبنى Omit من نوعي أداة آخرين:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

اقرأه من الداخل: keyof T هو union كل المفاتيح، وExclude<keyof T, K> يحذف المفاتيح غير المرغوبة من هذا union، وPick يُبقي المفاتيح المتبقية. لذا فإن Omit<User, "password"> هو Pick<User, "id" | "name" | "email">. ولأن Pick يمر على مفاتيح T، تبقى المُعدِّلات readonly و? على الخصائص المتبقية.

لاحظ القيد: K extends keyof any وليس K extends keyof T. هذه التفصيلة موضوع القسم التالي.

Omit لا يتحقق من وجود المفتاح

keyof any هو string | number | symbol، لذا يقبل Omit أي مفتاح على الإطلاق، بما فيه مفتاح لا يملكه T. فالخطأ الإملائي يُترجم ولا يحذف شيئًا:

لا خطأ في أي مكان، وتخرج كلمة المرور في JSON. اختير القيد الفضفاض عن قصد عندما أُضيف Omit في TypeScript 3.5. أسباب الفريق: في الكود العام كثيرًا ما لا تأتي المفاتيح المحذوفة من T (فدالة تدمج كائنين قد تعطي وسيط rest فيها النوع Omit<T1, keyof T2>)، وExclude لا يقيّد معامله الثاني أيضًا، ونسخة مقيّدة كانت ستكسر كثيرًا من أنواع Omit المكتوبة يدويًا الموجودة في المشاريع. أما في الكود اليومي فنسخة أكثر صرامة تلتقط الخطأ الإملائي:

index.ts(10,36): error TS2344: Type '"pasword"' does not satisfy the constraint 'keyof User'.
index.ts(11,7): error TS2741: Property 'password' is missing in type '{ id: number; name: string; }' but required in type 'PublicUser'.

الخطأ الأول هو المهم: يُفحص المفتاح مقابل keyof User. والثاني ناتج عنه، لأنه لم يُحذف شيء وما زال PublicUser يشترط password. صحّح الإملاء إلى "password" فيُترجم الكود. تعرّفه مشاريع كثيرة مرة واحدة في ملف أنواع مشترك وتستخدمه بدلًا من Omit.

استبدال نوع خاصية

لا يستطيع interface أن يرث من آخر ويغيّر خاصية إلى نوع غير متوافق: interface ApiUser extends User { id: string } يفشل بالخطأ error TS2430: Interface 'ApiUser' incorrectly extends interface 'User'. احذف الخاصية أولًا، ثم صرّح بها من جديد:

ومع type alias يكون الأمر نفسه intersection: type ApiUser = Omit<User, "id"> & { id: string }. ومن دون Omit كان الـ intersection User & { id: string } سيعطي id النوع number & string، وهو never.

Omit على أنواع union

Omit ليس توزيعيًا. على union يبدأ من keyof للـ union كله، وهو لا يحتوي إلا المفاتيح المشتركة بين كل الأعضاء، فتختفي الخصائص الخاصة بكل عضو:

index.ts(8,39): error TS2353: Object literal may only specify known properties, and 'radius' does not exist in type 'NewShape'.

أصبح NewShape هو { kind: "circle" | "square" }. لتحذف من كل عضو على حدة، طبّق Omit داخل نوع شرطي توزيعي:

الشرط T extends unknown ? ... : never صحيح دائمًا؛ وهو موجود فقط ليجعل النوع الشرطي يتوزع على union. تشرح صفحة الأنواع الشرطية سبب نجاح ذلك. استخدم DistributiveOmit كلما كان النوع الذي تحذف منه discriminated union.

حذف خاصية متداخلة

لا ينظر Omit إلا إلى مفاتيح المستوى الأعلى، لذا فإن Omit<Settings, "editor.wordWrap"> لا يحذف شيئًا (ولا يعترض، كما رأينا). لإسقاط خاصية في مستوى أدنى، احذف الأب ثم أضفه من جديد مع Omit خاص به:

Settings["editor"] نوع وصول مفهرس (indexed access type): يسمّي نوع الكائن المتداخل لكي يعمل عليه Omit.

حذف الخصائص وقت التشغيل

يغيّر Omit النوع فقط. لإنتاج كائن دون تلك الخصائص، أخرجها بالتفكيك، أو اكتب دالة مساعدة generic صغيرة نوع قيمتها المُعادة Omit:

تقيّد الدالة المساعدة K بـ keyof T، فالمفتاح المكتوب خطأً يكون خطأ ترجمة في موضع الاستدعاء. والنسخة سطحية: الكائنات المتداخلة مشتركة مع الأصل.

Omit أم Pick أم Exclude

يعمل علىيُبقيمثالالنتيجة
Omit<T, K>أنواع الكائناتكل خاصية عدا KOmit<User, "password">{ id; name; email }
Pick<T, K>أنواع الكائناتالخصائص التي في K فقطPick<User, "id" | "name">{ id; name }
Exclude<U, M>أنواع unionأعضاء union غير القابلة للإسناد إلى MExclude<"a" | "b" | "c", "a">"b" | "c"

اختر بين Omit وPick بحسب ما يجب أن يحدث عندما يكبر النوع المصدر. يضم Omit الخصائص الجديدة تلقائيًا، وهذا يناسب «كل شيء عدا السر». أما Pick فلا يضمها أبدًا، وهذا يناسب «هذه الحقول بالضبط» وهو أكثر أمانًا لكل ما يغادر خادمك. يفحص Pick مفاتيحه مقابل keyof T؛ أما Omit فلا.

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

ماذا يفعل Omit في TypeScript؟

يبني Omit<T, K> نوع كائن جديدًا فيه كل خصائص T عدا تلك التي مفاتيحها في K. فـ Omit<User, "password"> هو User دون password. إنه يغيّر النوع فقط؛ ولا يحذف شيئًا من الكائن وقت التشغيل.

كيف أحذف عدة خصائص في TypeScript؟

مرّر union من المفاتيح كوسيط ثانٍ: Omit<User, "password" | "createdAt" | "updatedAt">. يُحذف كل مفتاح في union.

ما الفرق بين Omit وExclude؟

يحذف Omit خصائص من نوع كائن: Omit<{ a: 1; b: 2 }, "a"> هو { b: 2 }. ويحذف Exclude أعضاء من نوع union: Exclude<"a" | "b", "a"> هو "b". وOmit منفّذ باستخدام Exclude: Pick<T, Exclude<keyof T, K>>.

لماذا لا يعطي Omit خطأ لمفتاح غير موجود؟

معامل المفاتيح فيه مقيّد بـ keyof any (أي string أو number أو symbol)، لا بـ keyof T، لذا فخطأ إملائي مثل Omit<User, "pasword"> يُترجم ولا يحذف شيئًا. عرّف type StrictOmit<T, K extends keyof T> = Omit<T, K> لتحصل على خطأ عند المفاتيح المجهولة.

كيف أحذف خاصية من كائن وقت التشغيل في TypeScript؟

استخدم التفكيك مع عنصر rest: const { password, ...rest } = user;. تعطي TypeScript المتغير rest نوع الكائن دون password، وهو الشكل نفسه لـ Omit<User, "password">، والخاصية تختفي فعلًا من الكائن الجديد.

Coddy programming languages illustration

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

ابدأ الآن