مقدمة: لماذا تحتاج إلى مفاتيح الواجهة كمصفوفة؟
تخيل: أنت تعمل على مشروع كبير باستخدام TypeScript، وتحتاج إلى المرور عبر جميع حقول كائن معقد، أو إنشاء نموذج ديناميكي في React، أو تصفية البيانات، أو ببساطة عرض قائمة بالخصائص المتاحة. المشكلة هي أن TypeScript هي طبقة فوق JavaScript، وأنواعها موجودة فقط أثناء التجميع. في وقت التشغيل، تختفي. فماذا تفعل؟ كيف تحصل على مفاتيح واجهة TypeScript كمصفوفة من السلاسل النصية لاستخدامها في الكود الفعلي؟
سؤال يقلق آلاف المطورين على Stack Overflow. اليوم، في عام 2025، سنستعرض جميع الأساليب الممكنة: من البسيطة والموثوقة إلى المتقدمة. ستتعلم كيف تجعل TypeScript يعمل لصالحك، وليس ضدك. هيا بنا!
المشكلة: الأنواع "المختفية"
دعنا ننظر فورًا إلى جذر المشكلة. لنفترض أن لديك هذه الواجهة:
interface User { id: number; name: string; email: string; isAdmin: boolean;}تحتاج إلى الحصول على مصفوفة من السلاسل النصية: ['id', 'name', 'email', 'isAdmin']. أول ما يتبادر إلى الذهن هو استخدام Object.keys(). ولكن هناك فارق بسيط. إذا حاولت كتابة هذا:
const keys = Object.keys({} as User);// النوع: string[]سيعيد لك TypeScript النوع string[]، وليس ('id' | 'name' | 'email' | 'isAdmin')[]. هذا لا معنى له - تفقد كل المعلومات حول المفاتيح المحددة.
لماذا يحدث هذا؟ لأن Object.keys() في JavaScript يعيد دائمًا مصفوفة من السلاسل النصية. لا يمكن لـ TypeScript أن يعرف على وجه اليقين أي المفاتيح سيكون للكائن أثناء التنفيذ (على سبيل المثال، إذا تم توسيع الكائن). لذلك، يعطي النوع الأكثر أمانًا، ولكنه عديم الفائدة.
الحل 1: المصفوفة "اليدوية" (البساطة والأمان)
الطريقة الأبسط والموصى بها لـ 90% من الحالات هي إنشاء مصفوفة يدويًا واستنتاج النوع منها. هذه ليست مزحة. يضمن هذا النهج أن تكون المصفوفة متزامنة دائمًا مع الواجهة.
// 1. نحدد مصفوفة المفاتيحexport const USER_KEYS = ['id', 'name', 'email', 'isAdmin'] as const;
// 2. نستنتج النوع من المصفوفةtype UserKey = typeof USER_KEYS[number]; // 'id' | 'name' | 'email' | 'isAdmin'
// 3. نستخدمها للواجهةinterface User { [key in UserKey]: key extends 'id' ? number : key extends 'name' ? string : key extends 'email' ? string : boolean;}
// 4. نحصل على مصفوفة السلاسل النصيةconst keys: string[] = [...USER_KEYS];console.log(keys); // ['id', 'name', 'email', 'isAdmin']الإيجابيات: كتابة كاملة، لا حيل سحرية، سهلة القراءة والصيانة.
السلبيات: تحتاج إلى صيانة مكانين (الواجهة والمصفوفة) يدويًا.
الحل 2: النوع المساعد مع keyof (متقدم، ولكن مع تحفظات)
يوفر TypeScript العامل keyof، الذي يعيد اتحاد المفاتيح (union type). ولكن كيف نحول هذا الاتحاد إلى مصفوفة سلاسل نصية؟ في عام 2025، لا يزال هذا يتطلب حيلة صغيرة.
الطريقة الكلاسيكية هي استخدام دالة مساعدة تتحقق من الأنواع أثناء التجميع:
// دالة مساعدةfunction getKeys<T>(obj: T): (keyof T)[] { return Object.keys(obj) as (keyof T)[];}
// الاستخدامinterface Product { title: string; price: number; inStock: boolean;}
const product: Product = { title: 'Keyboard', price: 100, inStock: true};
const productKeys = getKeys(product);// النوع: ("title" | "price" | "inStock")[]console.log(productKeys); // ['title', 'price', 'inStock']هام: تعمل هذه الطريقة فقط إذا كان لديك كائن حقيقي يطابق الواجهة. إذا كان لديك واجهة فقط، ولكن لا يوجد كائن، فسيتعين عليك إنشاء