مقدمة: أسطورة التحويل "السحري" للأنواع
\n\nكل مطور TypeScript يواجه عاجلاً أم آجلاً هذا الموقف: تحصل على JSON من الخادم، وتكون متأكداً من هيكله، وتقوم بتحويل بسيط باستخدام as. ثم... لا يعمل شيء. دوال الكلاس مفقودة، الحقول undefined، ويصبح التصحيح مهمة شاقة.
سؤال "How do I cast a JSON object to a TypeScript class?" هو من أكثر الأسئلة شيوعاً على Stack Overflow (عشرات الآلاف من المشاهدات). وهذا ليس مفاجئاً. TypeScript هي إضافة موجودة فقط أثناء التجميع. في وقت التشغيل، "الكلاس" الخاص بك هو مجرد كائن JavaScript عادي.
\n\nفي هذه المقالة، سنحلل 5 من أكثر الأخطاء شيوعاً وخطورة التي يرتكبها المطورون عند العمل مع JSON وكلاسات TypeScript. ستتعلم كيفية تجنبها، وستحصل على حلول جاهزة لكود الإنتاج.
\n\nالخطأ رقم 1: استخدام as للتحويل "الحقيقي"
\n\nأكثر فخ شائع هو الاعتقاد أن التركيب jsonData as MyClass ينشئ مثيلاً من الكلاس.
ماذا يحدث في الواقع؟
\nعامل as (أو صيغة الأقواس الزاوية) هو تأكيد نوع. يخبر المترجم: "ثق بي، أنا أعرف النوع". لكنه لا يقوم بأي سحر في JavaScript.
class User {\n name: string = '';\n greet(): string {\n return `مرحباً، ${this.name}!`;\n }\n}\n\nconst rawData = JSON.parse('{"name": "آنا"}');\nconst user = rawData as User;\n\nconsole.log(user.name); // "آنا" — يعمل\nconsole.log(user.greet()); // TypeError: user.greet is not a function\n\n\nلماذا؟ لأن rawData هو كائن عادي { name: 'آنا' }. ليس لديه النموذج الأولي User.prototype، وبالتالي لا يملك الدالة greet.
كيفية الإصلاح؟
\nقم دائماً بإنشاء مثيل حقيقي من الكلاس. الطريقة الأساسية هي Object.assign:
const user = Object.assign(new User(), rawData);\nconsole.log(user.greet()); // "مرحباً، آنا!"\n\n\nلكن لهذه الطريقة تفاصيل دقيقة سنناقشها لاحقاً.
\n\nالخطأ رقم 2: تجاهل الكائنات والمصفوفات المتداخلة
\n\nتستخدم Object.assign بسعادة، لكن بياناتك تحتوي على هياكل متداخلة. وهنا تبدأ المشكلة.
class Address {\n city: string = '';\n zip: string = '';\n}\n\nclass Company {\n name: string = '';\n address: Address = new Address();\n}\n\nconst data = JSON.parse('{"name": "TechCorp", "address": {"city": "موسكو", "zip": "101000"}}');\nconst company = Object.assign(new Company(), data);\n\nconsole.log(company.address.city); // "موسكو" — حظ موفق؟\nconsole.log(company.address instanceof Address); // false!\n\n\nالمشكلة: address بقي كائنًا بسيطًا، وليس مثيلاً من Address. إذا كان لدى Address دوال أو خصائص محسوبة، فستفقد.
الحل: التحويل التكراري
\nاكتب دالة مصنع أو استخدم مكتبة class-transformer:
import { plainToClass } from 'class-transformer';\n\nconst company = plainToClass(Company, data);\nconsole.log(company.address instanceof Address); // true\n\n\nclass-transformer يتجاوز جميع الحقول بشكل تكراري وينشئ مثيلات صحيحة من الكلاسات بناءً على الديكورات @Type.
الخطأ رقم 3: فقدان القيم الافتراضية
\n\nTypeScript يسمح بتعيين قيم افتراضية للحقول مباشرة في الكلاس. لكن عند "تحويل" JSON، قد يتم استبدال هذه القيم بـ undefined.
class Config {\n theme: string = 'light';\n fontSize: number = 14;\n language: string = 'ru';\n}\n\nconst partialData = JSON.parse('{"theme": "dark"}');\nconst config = Object.assign(new Config(), partialData);\n\nconsole.l