مقدمة: JSON وTypeScript — الثنائي المثالي للعمل مع البيانات
\nJSON (JavaScript Object Notation) هو تنسيق خفيف لتبادل البيانات أصبح المعيار الفعلي للتواصل بين العميل والخادم، وكذلك لتخزين التهيئات ونقل المعلومات المنظمة. TypeScript، كونه مجموعة شاملة من JavaScript مع كتابة صارمة للأنواع، يوفر أدوات قوية للعمل الآمن والمتوقع مع JSON. في هذه المقالة، سنشرح عمليًا كيفية تحليل (تحويل سلسلة JSON إلى كائن) وتسلسل (تحويل كائن إلى سلسلة JSON) البيانات في TypeScript بشكل صحيح، مع تجنب الأخطاء الشائعة واستخدام إمكانيات نظام الأنواع.
\n\nالأساسيات: JSON.parse() و JSON.stringify() في TypeScript
\nيرث TypeScript بالكامل الطرق المدمجة في JavaScript للعمل مع JSON: JSON.parse() و JSON.stringify(). ومع ذلك، فإن استخدامها في بيئة ذات أنواع صارمة يتطلب نهجًا خاصًا.
JSON.parse() — من سلسلة إلى كائن
\nتأخذ طريقة JSON.parse() سلسلة JSON وتعيد كائنًا. في TypeScript، النتيجة الافتراضية تكون من النوع any، مما يحرمنا من جميع مزايا كتابة الأنواع. لنلقِ نظرة على مثال:
const jsonString = '{"name": "TypeScript", "version": 5.3}';\nconst parsed: any = JSON.parse(jsonString);\nconsole.log(parsed.name); // يعمل، لكن لا يوجد إكمال تلقائي أو فحص للأنواع\nللحصول على نتيجة ذات نوع محدد، استخدم تأكيد النوع (type assertion):
\ninterface Language {\n name: string;\n version: number;\n}\n\nconst jsonString = '{"name": "TypeScript", "version": 5.3}';\nconst parsed = JSON.parse(jsonString) as Language;\nconsole.log(parsed.name.toUpperCase()); // الإكمال التلقائي يعمل!\n\nJSON.stringify() — من كائن إلى سلسلة
\nJSON.stringify() يقوم بتسلسل الكائن إلى سلسلة JSON. يتحقق TypeScript من أن الكائن المُمرر صالح للتسلسل (لا يحتوي على دوال، undefined، Symbol، إلخ).
interface User {\n id: number;\n name: string;\n isActive: boolean;\n}\n\nconst user: User = { id: 1, name: "Alice", isActive: true };\nconst jsonString = JSON.stringify(user);\nconsole.log(jsonString); // '{"id":1,"name":"Alice","isActive":true}'\n\nتقنيات متقدمة: كتابة الأنواع ومعالجة الأخطاء
\nعمليًا، نادرًا ما يكون العمل مع JSON مثاليًا. قد تأتي البيانات من الخادم بتنسيق غير متوقع، أو تحتوي على حقول إضافية، أو تفتقد إلى حقول إلزامية. دعنا نرى كيف يساعد TypeScript في التعامل مع هذه المشكلات.
\n\nإنشاء دالة عامة للتحليل الآمن
\nلنكتب دالة شاملة تأخذ سلسلة JSON وتعيد نتيجة ذات نوع محدد مع معالجة الأخطاء:
\nfunction safeParse<T>(json: string): T | null {\n try {\n return JSON.parse(json) as T;\n } catch (error) {\n console.error("خطأ في تحليل JSON:", error);\n return null;\n }\n}\n\n// الاستخدام\ninterface Product {\n id: number;\n title: string;\n price: number;\n}\n\nconst rawData = '{"id": 101, "title": "Laptop", "price": 999.99}';\nconst product = safeParse<Product>(rawData);\nif (product) {\n console.log(product.title); // "Laptop"\n}\n\nالتحقق من صحة البيانات باستخدام حراس الأنواع المخصصة
\nحتى بعد تأكيد النوع as T، لا يتحقق TypeScript من أن البيانات تتوافق فعليًا مع الواجهة. للتحقق في وقت التشغيل، استخدم حراس الأنواع (type guards):
function isProduct(obj: any): obj is Product {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n typeof obj.id === 'number' &&\n typeof obj.title === 'string' &&\n typeof obj.price === 'number'\n );\n}\n\nconst rawData = '{"id": "abc", "title": "Phone", "price": 599}';\nconst parsed = JSON.parse(rawData);\nif (isProd