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

JSON في TypeScript: نوع JSON.parse وتحويل JSON إلى interface

يعيد JSON.parse النوع any، لذلك يثق TypeScript بأي نوع تعطيه للنتيجة. تعرّف على طريقة تحديد نوع JSON المحلَّل، والتحقق منه بـ type guard، وتحويل عينة JSON إلى interface، واستيراد ملفات .json، وما يفعله JSON.stringify بالتواريخ وSet وundefined.

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

يعيد JSON.parse النوع any، لذلك يقبل TypeScript أي نوع تسند إليه النتيجة. هذا يجعل تحديد نوع JSON المحلَّل سطرًا واحدًا، ويعني أيضًا أن النوع وعد تقطعه أنت، لا شيء يفحصه المترجم:

في الكائن الثاني تكون age هي النص "41". ومع ذلك يسميها TypeScript number، لأن any يمكن إسنادها إلى أي شيء، ويطبع البرنامج 411. لـ JSON كتبه كودك نفسه قبل لحظة، تحديد النوع لا بأس به. أما للبيانات القادمة من طلب أو ملف أو من التخزين المحلي، فتحقق منها.

التحليل إلى unknown

تحديد نوع النتيجة كـ unknown يجعل المترجم يصر على فحص قبل استخدام أي خاصية:

الخطأ هو index.ts(4,13): error TS18046: 'data' is of type 'unknown'. وكل فحص تكتبه بعد ذلك يضيّق نوع data أكثر قليلًا.

التحقق بـ Type Guard

الـ type guard دالة تعيد value is User. عندما تعيد true يعامل TypeScript القيمة كـ User من تلك النقطة، والفحوص داخلها فحوص حقيقية وقت التشغيل:

يرمي JSON.parse نفسه SyntaxError مع النص المشوّه، لذلك يغلّفه الكود الحقيقي بـ try/catch أيضًا. مع البيانات الكبيرة أو المتداخلة تطول الحراسات المكتوبة يدويًا؛ ومكتبات المخططات مثل Zod أو Valibot تتيح لك التصريح بالشكل مرة واحدة واشتقاق أداة التحقق ونوع TypeScript معًا منه.

تحويل JSON إلى Interface في TypeScript

تحويل عينة JSON إلى أنواع عمل آلي. لهذه الاستجابة:

{
    "id": 42,
    "title": "Learn TypeScript",
    "done": false,
    "owner": { "id": 7, "name": "Ada" },
    "tags": ["study", "ts"],
    "dueDate": "2024-03-15T10:30:00Z",
    "notes": null
}

اربط كل قيمة بنوعها، وأعطِ الكائنات المتداخلة interface خاصة بها، وميّز ما يمكن أن يتغير:

لا تستطيع عينة واحدة أن تخبرك بالحقول الاختيارية أو التي تقبل null. انظر في عدة استجابات، أو في توثيق الواجهة البرمجية، قبل أن تستقر على ? و| null.

التواريخ والـ Reviver

ليس في JSON نوع للتاريخ، لذلك تصل التواريخ كنصوص. الوسيط الثاني لـ JSON.parse، الـ reviver، يُستدعى لكل مفتاح ويستطيع إعادة بنائها:

المعامل value في الـ reviver نوعه any، وكذلك النتيجة، لذلك ما زال النوع Order موثوقًا به لا مفحوصًا. يضيف JSON.stringify(order, null, 2) مسافة بادئة من مسافتين للناتج، ويعيد Date إلى نص ISO الخاص به.

JSON.stringify وما يفقده

نوع إعادة JSON.stringify هو string. والقيم التي يحوّلها لا تعود دائمًا كما كانت، ولا يحذّرك النوع:

القيمةبعد JSON.stringify
Dateنص ISO (عبر الدالة toJSON الخاصة به)
Map، Set{} (حوّلها أولًا بـ [...set] أو Object.fromEntries(map))
undefined والدوال والرموز داخل كائنيُحذف المفتاح
undefined والدوال والرموز داخل مصفوفةnull
undefined أو دالة أو رمز وحدهundefined، لا نص
NaN، Infinitynull
bigintيرمي TypeError

قواعد وقت التشغيل هي نفسها كما في JavaScript العادية، ومشروحة في JSON في JavaScript.

نوع لأي قيمة JSON

عندما يتعامل الكود مع JSON عشوائي، يصف نوع تعاودي بالضبط ما يمكن أن يحتويه JSON ويرفض القيم التي لا يمكن أن يحتويها:

دون التعليق يكون السطر الأخير خطأ ترجمة لأن كائن Date ليس JsonValue.

استيراد ملفات .json

يمكن استيراد ملف .json كوحدة، ويستنتج TypeScript نوعه من محتواه:

{ "name": "app", "port": 8080, "tags": ["a"] }
// CommonJS output, or a bundler
import config from "./config.json";
const port: number = config.port; // typed from the file: number

// An ES module under module: nodenext
import settings from "./config.json" with { type: "json" };

في TypeScript 7 يعمل هذا دون إعدادات إضافية عندما يكون module مضبوطًا على nodenext أو node20 أو commonjs أو esnext أو preserve. مع node16 وnode18 يفشل بالخطأ TS2732، Cannot find module './config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.، إلى أن تضيف "resolveJsonModule": true؛ وضبطه على false يعطّل استيراد JSON في كل مكان. في وحدة ES مع nodenext أو node20 يحتاج الاستيراد إلى السمة with { type: "json" } (الخطأ TS1543 دونها)، ولا يُسمح إلا بالاستيراد الافتراضي (الخطأ TS1544 مع import { port }). وينسخ tsc ملف .json المستورد إلى outDir بجوار JavaScript المُترجم.

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

ما النوع الذي يعيده JSON.parse في TypeScript؟

any. لا يستطيع المترجم معرفة محتوى نص ما، لذلك يُترجم const user: User = JSON.parse(text) مهما كان في النص. أسند النتيجة إلى unknown وتحقق منها عندما تأتي البيانات من خارج برنامجك.

كيف أحوّل JSON إلى interface في TypeScript؟

خذ عينة ممثِّلة واكتب خاصية لكل مفتاح: النصوص والأرقام والقيم المنطقية تقابلها string وnumber وboolean، والكائن المتداخل يصبح interface مستقلة، ومصفوفة الكائنات تصبح Item[]، والمفاتيح التي تغيب أحيانًا تأخذ ?. أدوات توليد الكود مثل quicktype تؤتمت ذلك، لكن تحقق من تخميناتها بأكثر من عينة واحدة.

كيف أستورد ملف JSON في TypeScript؟

يعمل import config from "./config.json"; في TypeScript 7 عندما يكون module مضبوطًا على nodenext أو node20 أو commonjs أو esnext أو preserve، ويُحدد نوع النتيجة من محتوى الملف. مع node16 أو node18 اضبط أيضًا "resolveJsonModule": true. وفي وحدة ES مع nodenext أو node20 أضف السمة التي تطلبها Node: import config from "./config.json" with { type: "json" };.

هل يعيد JSON.stringify نصًا دائمًا؟

نوعه يقول string، لكن JSON.stringify(undefined) وJSON.stringify(() => 1) يعيدان undefined وقت التشغيل. والقيم داخل الكائنات تُحوَّل أيضًا: يصبح Date نص ISO، ويصبح Map وSet الكائن {}.

Coddy programming languages illustration

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

ابدأ الآن