كان الخطأ الذي جعلني أتوقف أخيرًا عن الكتابة اليدوية لأنواع API صغيرًا بشكل محرج. عادت نقطة نهاية المدفوعات discount: null للعملاء الذين ليس لديهم واحدة، ولقد كتبتها discount: number لأن الرد الوحيد الذي نظرت إليه أثناء كتابة الواجهة كان عميلاً لديه خصم. كان TypeScript سعيدًا تمامًا. لم يكن لدى المترجم طريقة لمعرفة I'؛ كذب عليه. بعد ثلاثة أسابيع أ .toFixed(2) في هذا المجال، تم إنتاج مجموعة فرعية من المستخدمين الذين كانوا أقل أهمية في اختباري وأكثر أهمية في الفاتورة.
هذه هي المشكلة برمتها في كتابة واجهة برمجة التطبيقات يدويًا: أنت تكتب ما تريد يعتقد تعود نقطة النهاية، ويقوم المترجم بفرض اعتقادك بدلاً من الواقع. كل ضمان يقدمه لك TypeScript في اتجاه مجرى النهر هو جيد مثل تلك الواجهة المكتوبة بخط اليد، ولا يوجد شيء في سلسلة الأدوات يتحقق منه مقابل الاستجابة الفعلية. تحصل على كل مراسم الكتابة الثابتة دون أي أمان، وهو ما يمكن القول إنه أسوأ من عدم وجود أنواع على الإطلاق - على الأقل التعليمات البرمجية غير المكتوبة تجعلك متشككًا.
يؤدي إنشاء أنواع من حمولة حقيقية إلى قلب الاتجاه. بدلاً من وصف الشكل الذي تعتقده، فإنك تأخذ استجابة أرسلها الخادم بالفعل وتستمد الشكل منه. الإخراج ميكانيكي: لا يوجد تفاؤل، ولا توجد حقول نسيتها، ولا number حيث تقول البيانات number | null. أقوم ببناء [Toolz.dev](/وأضع متصفحًا قائمًا JSON إلى محول TypeScript هناك يفعل ذلك، لكن هذا الدليل يدور حول قواعد الاستدلال نفسها - ما يمكن للمولد اكتشافه، وما يمكنه تخمينه فقط، وأين لا يزال يتعين عليك التفكير فيه.
ليرة تركية؛DR: لتحويل JSON إلى TypeScript، استنتج كل مفتاح '؛s اكتب من قيمته (
string،number،boolean،null)، استخراج الكائنات المتداخلة في واجهاتها المسماة، ودمج صفائف الكائنات في واجهة عنصر واحد حيث يصبح أي مفتاح مفقود من بعض الأعضاء اختياريًا. قرر عمدا ما إذا كانnullوسائلkey?: Tأوkey: T | null- يعتمد هذا الاختيار على ما إذا كانت واجهة برمجة التطبيقات الخاصة بك تحذف الحقول الغائبة أو ترسلها على أنها فارغة. يعكس الاستدلال فقط العينة التي تقدمها، لذا استخدم حمولة تمثيلية مع عدة سجلات، وتعامل مع المخرجات كمسودة أولى تمت مراجعتها بدلاً من عقد نهائي.
لماذا يتم إنشاء أنواع TypeScript من JSON بدلاً من كتابتها؟
الإجابة الصادقة هي أن الأنواع المكتوبة بخط اليد تنجرف والأنواع التي تم إنشاؤها don'؛t. عندما تضيف الواجهة الخلفية حقلاً، تظل الواجهة المكتوبة بخط اليد خاطئة بصمت؛ لا توجد أخطاء، لأن الخصائص الإضافية في الاستجابة غير مرئية للنوع الذي لا يذكرها '؛t. عندما تتغير الواجهة الخلفية id من رقم إلى سلسلة، تستمر الواجهة الخاصة بك في الإصرار على ذلك '؛ ويستمر الرقم وTypeScript في الموافقة، حتى يتم تسلسل شيء ما بدلاً من إضافته.
هناك '؛s أيضًا حجة الملل البسيطة. تحتوي استجابة REST النموذجية على ثلاثين مفتاحًا عبر أربعة مستويات من التداخل. يستغرق نسخ ذلك يدويًا عشر دقائق من العمل الميكانيكي الخالص، والعمل الميكانيكي الذي يؤديه البشر به معدل عيب. سوف تقوم بخطأ اسم المفتاح. سوف تفوت الحقل الوحيد الذي '؛s مجموعة من الكائنات بدلاً من مجموعة من السلاسل. المولد لن.
لكن السبب الأقوى هو أن الجيل هو الذي يصنع الشكل مرئي. الصق الرد في المحول وسترى على الفور الأشياء التي قمت بتجاهلها أنت & #39؛ لقد تسللت إلى قراءة JSON الخام: ذلك metadata هو في الواقع كائن متداخل بعمق، ذلك tags يكون فارغًا في بعض الأحيان، حيث أن نصف المفاتيح الموجودة في قائمتك المرقمة مفقودة من بعض السجلات. الواجهة التي تم إنشاؤها عبارة عن ملخص للبنية الحقيقية للبيانات '؛s، وغالبًا ما تكون قراءتها أسرع طريقة لفهم نقطة النهاية التي قمت بها '؛t write. أنا '؛ لقد استخدمتها كخطوة توثيق أكثر من مرة على واجهات برمجة التطبيقات التي كانت مستنداتها كذبة.
حيث يتناسب هذا مع أدوات البيانات الأخرى الخاصة بك: إذا كنت '؛ قم بفحص الحمولة بدلاً من كتابتها، فإن تنسيق JSON هي المحطة الأولى الأفضل، وإذا كنت '؛ قم بمقارنة إجابتين لمعرفة ما تغير بين الإصدارات، فإن فرق JSON يجيب ذلك مباشرة.
كيف يعمل نوع الاستدلال من JSON فعليًا؟
لدى JSON ستة أنواع من القيم لكل RFC 8259: كائن، صفيف، سلسلة، رقم، true/false، و null. يتم تعيين الأنواع البدائية TypeScript'؛ على أربعة منها بشكل مباشر تقريبًا. العمل المثير للاهتمام موجود بالكامل في العملين الآخرين.
البدائيات تافهة. قيمة السلسلة تعني string. الرقم يعني number- لاحظ أن JSON لديه نوع رقمي واحد، لذلك لا توجد معلومات في البيانات تخبرك بما إذا كان 1 هو عدد صحيح أو عائم، وTypeScript لا '؛ يميز على أي حال. true أو false يتضمن boolean. هذا الجزء ليس به غموض.
تصبح الكائنات واجهات. تصبح كل قيمة كائن واجهة مسماة، والمفتاح الذي ظهر تحته يوفر الاسم، ويتم تحويله إلى PascalCase. مفتاح owner ينتج interface Owner. يتكرر التعشيش: كائن داخل كائن ينتج واجهة ثانية مرجعية من الأولى. وهذا يهم أكثر مما يبدو. البديل - تضمين كل شكل متداخل بشكل مجهول - ينتج إعلانًا واحدًا غير قابل للقراءة ولا يمنحك أي شيء قابل للاستيراد
// Inlined: technically correct, practically useless
interface Project {
owner: { id: number; email: string; twoFactor: boolean }
}
// Extracted: you can import and reference Owner on its own
interface Project {
owner: Owner
}
interface Owner {
id: number
email: string
twoFactor: boolean
}
مرة واحدة Owner موجود كاسم، ويمكن كتابة وظيفة تأخذ المالك فقط (owner: Owner) => void. مع النسخة المضمنة أنت '؛د الكتابة Project['owner'] في كل مكان، وهو ما يعمل ولكن يقرأ بشكل سيء.
المصفوفات هي المكان الذي تعيش فيه القرارات الحقيقية. نوع المصفوفة '؛s هو اتحاد أنواع عناصرها، لذا [1, 2, 3] يعطي number[] و [1, "a"] يعطي (number | string)[]. لاحظ الأقواس في تلك الثانية - بدونها، number | string[] يعني شيئا مختلفا تماما (رقم أو مجموعة من السلاسل)، والمولدات التي تنسى رمز الإصدار هذا الذي يجمع الشيء الخطأ ولكنه يصفه.
المصفوفات الفارغة هي طريق مسدود صادق. "tags": [] يخبرك بوجود مفتاح ويحمل مصفوفة؛ لا يخبرك شيئًا عما يحدث فيه. الإخراج الصحيح هو unknown[]، ويجب أن تقرأ ذلك لأن المولد يرفض التخمين وليس كإجابة نهائية. املأها بنفسك من الوثائق، أو ابحث عن عينة حيث يكون المصفوفة '؛t فارغة.
لماذا يتم دمج صفائف الكائنات بدلاً من توحيدها؟
هذا هو القرار الوحيد الذي يفصل المولد الذي تستخدمه '؛d عن مولد أنت '؛d الذي يتم التخلي عنه بعد خمس دقائق.
خذ بعين الاعتبار الاستجابة المرقّمة حيث تكون السجلات '؛ وموحدة تمامًا - وهذا يعني أن كل استجابة مرقمة حقيقية:
{
"rows": [
{ "id": 1, "name": "Ada", "nickname": "The Countess" },
{ "id": 2, "name": "Grace" }
]
}
تعامل مع كل عنصر بشكل مستقل وستحصل على اتحاد بين واجهتين: rows: (Row1 | Row2)[]. هذا هو تقنيا القراءة الأكثر دقة للعينة، وهي عديمة الفائدة. كل الوصول إلى row.nickname يتطلب الآن التضييق، لأن TypeScript يمكنه '؛ تعرف على عضو الاتحاد الذي لديك. قم بتوسيع ذلك إلى استجابة مكونة من خمسين سجلًا مع عدة حقول اختيارية وستحصل على اتحاد يضم العشرات من الواجهات شبه المتطابقة. لا أحد يريد ذلك.
القراءة المفيدة هي أن هذين الكائنين هما مثيلان لكيان واحد، و nickname هو حقل جريس '؛ لديه:
interface Row {
id: number
name: string
nickname?: string
}
interface T {
rows: Row[]
}
دمج That'؛s: اجمع كل مفتاح يتم رؤيته عبر جميع العناصر، وقم بوضع علامة على مفتاح اختياري إذا كان '؛s غائبًا عن أي منها. فهو يطابق كيفية إنتاج البيانات فعليًا - جدول قاعدة بيانات واحد، ومسلسل واحد، وبعض الأعمدة الفارغة - وينتج أنواعًا يمكنك استخدامها بدون احتفال. اسم عنصر المصفوفة مفرد أيضًا، لذا releases الغلة Release بدلاً من Releasesلأن releases: Releases[] يقرأ مثل خطأ حتى عندما يكون '؛t.
المقايضة حقيقية وتستحق أن نذكرها بوضوح: الدمج يفترض أن المصفوفة متجانسة. إذا كان لديك مصفوفة غير متجانسة حقًا - وهي عبارة عن خلاصة لأحداث ذات أشكال مختلفة، يتم تمييزها بواسطة أ type الحقل - يؤدي دمج المتغيرات المميزة إلى تسطيحها في واجهة واحدة حيث يكون كل شيء تقريبًا اختياريًا. هذا '؛ هو النموذج الخاطئ، وهو '؛s حالة يجب عليك فيها أخذ المخرجات التي تم إنشاؤها كنقطة بداية وكتابة اتحاد مميز مناسب يدويًا. المولدات لا & #39؛ تعرف مجالك. يقوم هذا بدمج الكائنات ودمج كل شيء آخر، وهو أمر صحيح في معظم الأوقات وخاطئ بطريقة يمكنك اكتشافها على الفور.
هل يجب أن يصبح null مفتاحًا اختياريًا أو عضوًا في النقابة؟
يمكن الدفاع عن كلا الاصطلاحين والفرق لاذع، لذا قرر الغرض بدلاً من قبول أي شيء تقوم أداتك الافتراضي به.
نظرا { "retiredAt": null }، هناك قراءتان:
interface A { retiredAt?: string } // the field may be absent
interface B { retiredAt: string | null } // the field is present and may be null
فهي غير قابلة للتبديل. في A، retiredAt يكون string | undefined وقد لا يكون المفتاح موجودًا على الكائن على الإطلاق. في Bالمفتاح موجود دائمًا، وقد تكون قيمته null. تحت strictNullChecks- الذي دليل TypeScript يوصي وما يجب أن ترتديه - كلاهما يجبرك على التعامل مع حالة الغياب، لكنهما يفرضان فحوصات مختلفة ويتسلسلان بشكل مختلف. JSON.stringify يغفل undefined الخصائص بالكامل وتنبعث null بالنسبة للكلام الفارغ، ينتشر الاختيار على طول الطريق إلى السلك.
تعتمد الإجابة الصحيحة على السلوك الفعلي لـ API'؛s، والذي لا يمكن لأي مولد رؤيته من عينة واحدة
| سلوك API و#39؛s | النموذج الصحيح | لماذا |
|---|---|---|
| يحذف المفتاح عندما يكون هناك '؛s لا قيمة لها | key?: T |
المفتاح الحقيقي هو '؛ هناك؛ اختياري دقيق |
يرسل المفتاح دائما، null عندما تكون فارغة |
key: T | null |
المفتاح موجود دائمًا؛ ? من شأنه أن يسمح بالغياب بشكل خاطئ |
| غير متسق - يتم حذفه أحيانًا، وأحيانًا يكون فارغًا | key?: T | null |
كلتا الحالتين حقيقيتان؛ نموذج كلاهما |
يرسل null فقط على ردود الخطأ |
ولا - قم بنمذجة الخطأ بشكل منفصل | الحقل الفارغ يخفي اتحاد أشكال الاستجابة |
هذا الصف الأخير هو الذي يستحق التوقف مؤقتًا. الحقل الذي يصبح خاليًا فقط في حالات الفشل هو إشارة إلى أن نقطة النهاية تُرجع شيئين مختلفين يرتديان شكلًا واحدًا، والإصلاح عبارة عن اتحاد مميز في حقل الحالة، وليس خاصية قابلة للإلغاء. نوع الجيل يظهر هذا النمط؛ لا '؛ وحلها.
المحول الافتراضي إلى key?: T نظرًا لأن حذف-عند-غياب هو التقليد الأكثر شيوعًا في واجهات برمجة تطبيقات JSON I'؛ لقد عملت معها، ولأنها تتكون بشكل أفضل من دمج المصفوفة الموصوفة أعلاه (مفتاح مفقود من بعض السجلات ومفتاح '؛s فارغ في يتم تصميم بعض السجلات بنفس الطريقة). قم بإيقاف تشغيل الخيار و null يبقى في الاتحاد بدلا من ذلك. ولا خدعة؛ اختر ما تفعله واجهة برمجة التطبيقات الخاصة بك بالفعل.
ماذا عن المفاتيح التي تمثل '؛t معرفات TypeScript صالحة؟
مفاتيح كائنات JSON عبارة عن سلاسل عشوائية. أسماء خصائص TypeScript عارية key: T الموضع ليس كذلك - يجب أن تكون معرفات صالحة. لذا "content-type"، "2fa"، "user.name"، و "" هي جميع مفاتيح JSON القانونية التي لا يمكن كتابتها بدون اقتباس في الواجهة.
الإصلاح هو الاقتباس، وهو '؛s ليس حلاً بديلاً - أسماء الخصائص المقتبسة هي TypeScript عادية
interface Headers {
"content-type": string
"2fa": boolean
class: string
}
يتم الوصول إلى هذه الخصائص من خلال تدوين القوس (headers["content-type"])، وهو أكثر تفصيلاً قليلاً ولكنه آمن تمامًا من حيث النوع. لاحظ أن class dos'؛t بحاجة إلى الاقتباس: الكلمات المحجوزة قانونية تمامًا مثل أسماء الممتلكات، على الرغم من أنها '؛ غير قانونية كمعرفات. ينطبق التقييد فقط عندما يتوقع TypeScript معرفًا - ولهذا السبب تحتاج الكلمة نفسها إلى المعالجة عندما تصبح واجهة اسم.
تحتاج أسماء الواجهات المشتقة من هذه المفاتيح إلى عمل أكثر من الاقتباس. 2fa حالات باسكال إلى 2fa، والذي يمكنه '؛t بدء معرف، لذلك يحصل على بادئة. كائنان متداخلان مختلفان كلاهما تحت المفاتيح المسماة owner كلاهما يريد أن يكون Owner، فيصبح الثاني Owner2. هذه تفاصيل غير ساحرة، وهي '؛ هي بالضبط التفاصيل التي تقرر ما إذا كان الإخراج الذي تم إنشاؤه يتم تجميعه أو يحتاج إلى خمسة عشر دقيقة من الإصلاح اليدوي قبل أن يتم ذلك. الاختبار الذي أحمل المحول عليه بسيط: لصق أي شيء صالح، ويجب تجميع الإخراج تحته strict بدون تعديلات.
واجهات أم اكتب الأسماء المستعارة؟
المولد ينبعث إما. الاختلافات العملية ضيقة ولكنها حقيقية، ومن المحتمل أن قاعدة التعليمات البرمجية الخاصة بك لديها بالفعل رأي مشفر في تكوين الوبر الخاص بها.
interface User {} يدعم دمج الإعلانات - أعلن عن نفس اسم الواجهة مرتين ويجمع TypeScript بينهما. That'؛s ضرورية لزيادة الأنواع من المكتبات التي لا تملكها '؛t control، ومسدس القدم في كل مكان آخر، حيث يتم دمج إعلانين غير مرتبطين بنفس الاسم بصمت بدلاً من الخطأ. تدعم الواجهات أيضًا extends، والذي ينتج رسائل خطأ أفضل قليلاً من أنواع التقاطع عند فشل القيد.
type User = {} can'؛t الدمج، والذي عادةً ما يكون ميزة، وit'؛s مطلوب لأي شيء يمثل '؛ شكل كائن: الاتحادات، الصفوف، الأنواع المعينة، الأنواع الشرطية. الجذر الذي هو '؛ كائن JSON - مجموعة من الأرقام، سلسلة عارية - لا يمكن التعبير عنه إلا كاسم مستعار، لذلك type Nums = number[] هو ما تحصل عليه بغض النظر عن الإعداد.
بالنسبة لأنواع واجهة برمجة التطبيقات التي تم إنشاؤها، فإنني أميل نحوها interface، ويرجع ذلك في الغالب إلى أن رسائل الخطأ أفضل بشكل هامشي ولأن خطر الدمج يكون نظريًا عندما يكون كل اسم موجودًا في ملف واحد تم إنشاؤه. لكن هذا قريب من قلب العملة، والاتساق مع التعليمات البرمجية المحيطة به مهم أكثر من المزايا. إذا كان تكوين ESLint الخاص بك موجودًا @typescript-eslint/consistent-type-definitions اضبط في كلتا الحالتين، وقم بمطابقتها وتوقف عن التفكير فيها.
كيف يختلف هذا عن مخطط JSON إلى TypeScript؟
هذه تحل مشكلات مختلفة حقًا، وتستحق '؛s الدقة، لأن "؛JSON إلى TypeScript"؛ و"؛ مخطط JSON إلى TypeScript"؛ هي كلمة واحدة متباعدة وكثيرا ما يتم الخلط بينها.
JSON إلى TypeScript هو الاستدلال من مثال. الإدخال: قيمة. يلاحظ المولد ما '؛ هناك ويعمم. لا يمكنه معرفة ما إذا كان الحقل مطلوبًا، أو ما إذا كانت السلسلة مقيدة بالتعداد، أو ما إذا كان الرقم له حد أدنى، أو ما إذا كانت العينة الوحيدة التي قمت بلصقها ممثلة. It'؛s الاستقراء من ملاحظة واحدة، مع كل ما ينطوي عليه.
مخطط JSON إلى TypeScript هو ترجمة من إعلان. الإدخال: أ مخطط JSON الوثيقة، التي تنص بالفعل على الأنواع، required المصفوفات والتعدادات والتنسيقات والقيود. المولد هو '؛ التخمين - it'؛s الترجمة الصوتية لعقد موجود إلى بناء جملة TypeScript. required خرائط لخصائص غير اختيارية؛ أ enum خرائط لسلسلة الاتحاد الحرفي؛ oneOf خرائط لنوع الاتحاد.
القاعدة تتبع مباشرة: في حالة وجود مخطط، استخدمه. مخطط JSON، مواصفات OpenAPI، أ .proto يعد الملف أو مخطط GraphQL موثوقًا بطريقة لا تكون فيها الاستجابة التي تم أخذ عينات منها أبدًا. الاستدلال هو ما تصل إليه في حالة عدم وجود مخطط - نقطة نهاية داخلية غير موثقة، وواجهة برمجة تطبيقات تابعة لجهة خارجية تكون مستنداتها قديمة، وتنسيق ملف تكوين نما بشكل عضوي، وهو عنصر أساسي أنت & #39؛ إعادة كتابة الاختبارات ضده. والذي، من باب الإنصاف، يصف جزءًا كبيرًا من JSON الذي يتعامل معه أي منا بالفعل.
هناك '؛s طريق وسط جدير بالذكر: استخدم الاستدلال لـ التمهيد، ثم الحفاظ عليها باليد. قم بإنشاء الواجهة من استجابة حقيقية للحصول على الشكل وأسماء الحقول بشكل صحيح، ثم قم بتحريرها - قم بتشديد أ string إلى اتحاد حرفي حيث تعرف القيم المسموح بها، قم بإصلاح unknown[] تركت العينة فارغة، وقسمت الواجهة المدمجة إلى اتحاد مميز مناسب. يقوم المولد بإجراء 90% ميكانيكيًا ويمكنك تطبيق معرفة المجال التي لا يمكن أن يمتلكها هيكليًا.
أين الاستدلال يخطئ؟
قائمة قصيرة وصادقة. كل واحد من هذه الأمور هو قيد على النهج، وليس خطأ في أداة معينة، ومعرفة هذه الأخطاء هي الفرق بين استخدام الأنواع التي تم إنشاؤها جيدًا والحرق بها.
العينات الفردية لا تحدد النوع بشكل كافٍ. الحقل الذي '؛s number في عينتك قد يكون null في 5% من السجلات. قد يكون الحقل الموجود في السجلات الثلاثة التي قمت بلصقها '؛ اختياريًا عبر مجموعة البيانات الكاملة. يُبلغ الاستدلال عما رآه. الصق المزيد من السجلات - من الناحية المثالية صفحة حقيقية من النتائج بدلاً من كائن واحد تم اختياره بعناية - وتصبح الخيارات أكثر دقة بشكل مفيد.
تخفي السلاسل أنواعها الحقيقية. الطوابع الزمنية ISO وUUIDs وعناوين URL وعناوين البريد الإلكتروني كلها فقط string إلى محلل JSON. "2026-07-16T09:00:00Z" هو تاريخ لغويا؛ لا شيء في البيانات يقول ذلك. إذا كانت قاعدة التعليمات البرمجية الخاصة بك تحتوي على علامة تجارية ISODateString اكتب، أنت '؛re استبداله يدويًا.
تفقد الأرقام الفروق الدقيقة. نوع الرقم الفردي JSON'؛ يعني معرفًا يصل عدد صحيح 64 بت على الخادم '؛ كرقم JavaScript وربما فقد الدقة بالفعل قبل أن يراه المولد الخاص بك - Number.MAX_SAFE_INTEGER يبلغ حوالي 9×10¹5، وقد تعلم تويتر هذا بالطريقة الصعبة. إذا كانت واجهة برمجة التطبيقات الخاصة بك ترسل أعدادًا صحيحة كبيرة كسلاسل، فهذا '؛ لماذا، وتم إنشاؤها string صحيح.
تبدو القيم الحرفية مثل أنواعها العامة. "status": "active" يستنتج stringلا "active" | "archived" | "pending". النوع الأضيق أكثر فائدة ولا يمكن لأي عينة إثبات ذلك. هذا هو التعديل اليدوي الأكثر شيوعًا الذي أقوم به للمخرجات التي تم إنشاؤها.
الحاويات الفارغة لا تقول شيئا. [] يعطي unknown[] و {} يعطي واجهة فارغة. كلاهما المولد صادق.
لا شيء من هذا يجعل الاستدلال غير آمن - فهو يجعله مسودة. سير العمل الذي يعمل هو: إنشاء، قراءة المخرجات بعناية، إصلاح الأشياء الأربعة أو الخمسة التي تعرفها والتي يمكن للعينة '؛ لنفترض، الالتزام بها. لا يزال هذا '؛ أسرع وأكثر دقة من نسخ ثلاثين مفتاحًا يدويًا، وهو البديل الفعلي.
هل يتم تحميل JSON الخاص بي في أي مكان؟
لا، وهذه فئة من الأدوات حيث يستحق السؤال إجابة حقيقية وليس شارة.
فكر في ماذا '؛s في JSON you'؛d لصق في مولد النوع. It'؛s استجابة واجهة برمجة التطبيقات، مما يعني أنها تحتوي بشكل معقول على رمز حامل، ومعرف جلسة، وبريد إلكتروني للعميل، ومعرف مستخدم داخلي، وطبقة تسعير، وسر خطاف الويب. That'؛s ليست افتراضية - it'؛s الحالة المشروطة، لأن بيت القصيد هو أنك أمسكت بـ حقيقي الرد على النوع ضد.
يتلقى أي محول من جانب الخادم بالضرورة تلك الحمولة. قد لا يقوم بتسجيله، وربما لا يفعل ذلك '؛t، لكنك '؛ تقوم بتوسيع الثقة التي لا تثق بها '؛t يجب أن تمتد، واعتمادًا على البيانات، قد تقوم بإنشاء مشكلة امتثال لمهمة ليس لها أي عمل لمس الشبكة على الإطلاق.
استنتاج النوع هو حساب خالص على قيمة تم تحليلها. لا يحتاج إلى شبكة ولا حساب ولا تخزين. المحول الموجود على Toolz.dev عبارة عن بضع مئات من الأسطر من TypeScript الخالي من التبعية الذي يعمل في علامة التبويب الخاصة بك؛ الحمولة عبارة عن سلسلة JavaScript في متصفحك و#39؛ وذاكرة ويبقى هناك. يمكنك التحقق من ذلك بالطريقة التي تقوم بها أنت و#39؛d التحقق من أي مطالبة من هذا القبيل - افتح علامة تبويب الشبكة واضغط على إنشاء أو إيقاف تشغيل شبكة wifi الخاصة بك وشاهدها وهي تستمر في العمل. هذا هو نفس المبدأ وراء كل أداة على الموقع، وI'؛ لقد كتبت عن سبب أهميتها على نطاق أوسع لماذا تتفوق الأدوات المستندة إلى المتصفح على الأدوات الموجودة على جانب الخادم للحصول على بيانات حساسة.
مثال عملي
هنا #39؛s عينة الأدوات التي يتم شحنها بها، والتي تم إنشاؤها عمدًا لممارسة كل قاعدة أعلاه
{
"id": 4821,
"name": "Toolz",
"isPublic": true,
"retiredAt": null,
"owner": {
"id": 12,
"email": "[email protected]",
"twoFactor": false
},
"tags": ["developer", "privacy", "browser"],
"releases": [
{ "version": "1.0.0", "downloads": 1420, "notes": "First cut" },
{ "version": "1.1.0", "downloads": 3310 }
]
}
مع الجذر المسمى Project، وهذا يولد:
export interface Project {
id: number
name: string
isPublic: boolean
retiredAt?: null
owner: Owner
tags: string[]
releases: Release[]
}
export interface Owner {
id: number
email: string
twoFactor: boolean
}
export interface Release {
version: string
downloads: number
notes?: string
}
اقرأ ما حدث. owner تم استخراجه في الواجهة الخاصة به والإشارة إليه بالاسم. tags انهار ل string[] لأن كل عنصر كان سلسلة. releases دمجت أعضائها في عضو واحد Release- متفرد - و notes أصبح اختياريًا لأن الإصدار الثاني يحتوي على '؛t. retiredAt أصبحت اختيارية لأن قيمتها الوحيدة المرصودة كانت فارغة.
والآن اقرأ ما أنت '؛d الإصلاح. retiredAt?: null هل المولد '؛s تقرير صادق أنه لم ير أبدًا قيمة غير فارغة، وit'؛s عديمة الفائدة كنوع - you'؛d قم بتغييره إلى retiredAt?: string لأنك تعرف ذلك '؛s طابع زمني عند وجوده. هذا التعديل الفردي هو الدرس بأكمله: حصل المولد على البنية المكونة من سبعة مفاتيح، والتداخل، ودمج المصفوفة، والاختيارية في عجينة واحدة، وترك لك القرار الوحيد الذي يتطلب معرفة ما يعنيه الحقل.
أسئلة وأجوبة
كيف أقوم بتحويل JSON إلى واجهة TypeScript؟
الصق JSON الخاص بك في المحول، وقم بتعيين اسم نوع الجذر على أي مورد يسمى، ثم اضغط على إنشاء. فهو يستنتج نوع كل مفتاح، ويسحب الكائنات المتداخلة إلى واجهاتها المسماة، ويدمج صفائف الكائنات في نوع عنصر واحد، ويخرج التعليمات البرمجية التي يمكنك نسخها مباشرة إلى ملف .ts ملف. هناك '؛ لا يوجد تسجيل ولا تحميل - يتم تشغيل الاستدلال في متصفحك.
ماذا يحدث لصفائف الكائنات؟
هم '؛ تم دمجها في واجهة واحدة تصف عنصرًا واحدًا، ويتم كتابة الخاصية كمصفوفة منه. أي مفتاح يظهر في بعض أعضاء المصفوفة دون البعض الآخر يصبح اختياريًا. وهذا يطابق كيفية تصرف البيانات المرقمة الحقيقية، حيث تأتي السجلات من جدول واحد وبعض الأعمدة لا يمكن إبطالها. الحالة الوحيدة التي تتعامل معها بشكل سيء هي مجموعة غير متجانسة حقًا من أنواع الأحداث المختلفة، والتي يجب عليك تحويلها يدويًا إلى اتحاد مميز.
هل يجب أن يصبح null مفتاحًا اختياريًا أم اتحادًا مع null؟
يعتمد ذلك على ما إذا كانت واجهة برمجة التطبيقات الخاصة بك تحذف الحقول الغائبة أو ترسلها على أنها فارغة. إذا كان يحذفهم، key?: T دقيق. إذا كان المفتاح موجودًا دائمًا وأحيانًا فارغًا، key: T | null دقيقة، وتستخدم ? سيسمح بشكل خاطئ بفقدان المفتاح. يتم تعيين المحول افتراضيًا على أنه اختياري ويتيح لك التبديل، لأن الاثنين يتسلسلان بشكل مختلف - JSON.stringify يسقط خصائص غير محددة ولكنه يصدر خصائص فارغة.
هل يمكنه استنتاج أنواع دقيقة من عينة JSON واحدة؟
يستنتج أنواعًا دقيقة لتلك العينة، وهو '؛ نفس الشيء. قد يكون الحقل الذي '؛s رقمًا في سجلك الواحد فارغًا في السجلات الأخرى؛ قد يكون الحقل الموجود في جميع السجلات الثلاثة التي قمت بلصقها اختياريًا عبر مجموعة البيانات الكاملة. استخدم حمولة تمثيلية تحتوي على عدة سجلات بدلاً من كائن واحد تم اختياره بعناية، وتعامل مع المخرجات كمسودة تمت مراجعتها بدلاً من عقد نهائي.
ماذا '؛s الفرق بين JSON إلى TypeScript وJSON Schema إلى TypeScript؟
تستنتج هذه الأداة الأنواع من قيمة المثال؛ يقوم مخطط JSON إلى TypeScript بترجمة مخطط رسمي يعلن بالفعل عن الأنواع والحقول المطلوبة والتعدادات. المخطط موثوق والاستدلال هو تخمين، لذلك إذا كان لديك مخطط JSON أو مواصفات OpenAPI أو مخطط GraphQL، فاستخدمه. الاستدلال مخصص للحالة الشائعة جدًا حيث لا يوجد مخطط وكل ما لديك هو نص الاستجابة.
كيف يتعامل مع المفاتيح التي تمثل '؛t معرفات صالحة؟
يتم اقتباس المفاتيح ذات الشرطات أو النقاط أو المسافات أو الأرقام البادئة في الإخراج، لذلك "content-type" يصبح "content-type": string. That'؛s typeScript صالح، ويمكن الوصول إليه من خلال تدوين الأقواس. الكلمات المحجوزة مثل class دون'؛تحتاج إلى الاقتباس كأسماء خصائص. أسماء الواجهات المشتقة من هذه المفاتيح هي PascalCased ومسبقة إذا كانت '؛d تبدأ برقم، والأسماء المتصادمة تحصل على لاحقة رقمية بحيث يتم تجميع الإخراج دائمًا.
هل يجب علي إنشاء واجهات أو كتابة أسماء مستعارة؟
قم بمطابقة ما تفعله قاعدة التعليمات البرمجية الخاصة بك بالفعل - وهذا في الغالب سؤال اتساق. الواجهات تدعم دمج الإعلان و extends، وإعطاء رسائل خطأ أكثر وضوحًا بشكل هامشي. اكتب الأسماء المستعارة can'؛t الدمج، وهو أمر مرغوب فيه عادةً، وهو مطلوب لأي شيء يمثل '؛t شكل كائن. يتم إصدار الجذر that'؛s مصفوفة أو بدائية كاسم مستعار في كلتا الحالتين، حيث لا يوجد كائن للإعلان عن واجهة له.
هل تم تحميل json الخاص بي على خادم؟
لا. يعمل محرك الاستدلال بالكامل كجافا سكريبت في متصفحك، بدون مكالمات شبكة، ولا تسجيل، ولا تخزين. وهذا مهم هنا أكثر من معظم الأدوات، لأن JSON you'؛ عادةً ما يكون لصق d في منشئ النوع بمثابة استجابة API حقيقية تحتوي على رموز مميزة أو سجلات عملاء أو معرفات داخلية. افتح علامة تبويب الشبكة أثناء إنشاء الإنترنت أو قطع الاتصال به - فهو يستمر في العمل.
الأدوات ذات الصلة: تنسيق JSON لفحص الحمولة أولا، JSON إلى YAML و json إلى xml لتحويل التنسيق، و فرق JSON لاكتشاف ما تغير بين إجابتين. مزيد من القراءة: الدليل النهائي لأدوات JSON و دليل أدوات الترميز للمطور '؛s.



