הבאג שגרם לי סוף סוף להפסיק לכתוב ביד סוגי API היה קטן בצורה מביכה נקודת קצה תשלומים חזר discount: null ללקוחות בלי אחד, והקלדתי אותו discount: number כי התגובה האחת שהסתכלתי עליה בזמן כתיבת הממשק הייתה במקרה לקוח עם הנחה.TypeScript היה מאושר לחלוטין למהדר לא הייתה דרך לדעת I'd שיקר לזה. שלושה שבועות לאחר מכן א .toFixed(2) על השדה הזה זרקו ייצור בדיוק עבור תת-קבוצת המשתמשים שהכי פחות חשובה לבדיקה שלי והכי הרבה לחשבונית.
זו כל הבעיה בהקלדת API ביד: אתה מקליד מה שאתה להאמין נקודת הקצה חוזרת, והמהדר אוכף בצייתנות את האמונה שלך ולא את המציאות כל אחריות ש-TypeScript נותן לך במורד הזרם טובה רק כמו אותו ממשק ראשון בכתב יד, ואין שום דבר בשרשרת הכלים שבודק אותו מול תגובה ממשית אתה מקבל את כל הטקס של הקלדה סטטית ללא שום בטיחות, וזה ללא ספק גרוע יותר מאשר ללא טיפוסים בכלל - לפחות קוד לא מוקלד גורם לך לחשוד.
יצירת סוגים ממטען אמיתי הופכת את הכיוון במקום לתאר מה אתה חושב שהצורה, אתה לוקח תגובה שהשרת שלח בפועל ומפיק ממנה את הצורה הפלט הוא מכני: אין אופטימיות, אין שדות ששכחת שקיימים, אין number איפה הנתונים אומרים number | null. אני בונה [Toolz.dev] (/ ושם מבוסס דפדפן ממיר JSON ל-TypeScript שם זה עושה את זה, אבל המדריך הזה עוסק בכללי ההסקה עצמם - מה מחולל יכול להבין, על מה הוא יכול רק לנחש, ואיפה אתה עדיין צריך לחשוב.
TL;DR: כדי להמיר JSON ל-TypeScript, הסיקו כל מפתח 's הקלד מהערך שלו (
string,number,boolean,null), חלץ אובייקטים מקוננים לתוך הממשקים בעלי השם שלהם, ומיזוג מערכים של אובייקטים לממשק אלמנט אחד שבו כל מפתח שחסר בחלק מהאיברים הופך לאופציונלי. להחליט בכוונה אםnullאמצעיםkey?: Tאוkey: T | null- בחירה זו תלויה בשאלה אם ה-API שלך משמיט שדות נעדרים או שולח אותם כ-null. מסקנות משקפות רק את המדגם שאתה מספק, אז השתמש במטען מייצג עם מספר רשומות, והתייחס לפלט כאל טיוטה ראשונה שנבדקה ולא כחוזה מוגמר.
למה ליצור סוגי TypeScript מ-JSON במקום לכתוב אותם?
התשובה הכנה היא שסוגים בכתב יד נסחפים וסוגים שנוצרו don't. כאשר הקצה האחורי מוסיף שדה, הממשק בכתב יד שלך נשאר שגוי בשקט; שום דבר לא טועה, כי מאפיינים נוספים בתגובה אינם נראים לסוג שעושה ' לא להזכיר אותם. כאשר הקצה האחורי משתנה id ממספר למחרוזת, הממשק שלך ממשיך להתעקש עליו ' מספר ו-TypeScript ממשיך להסכים, עד שמשהו משרשר במקום מוסיף.
There's גם טיעון השיעמום הפשוט תגובה REST טיפוסי יש שלושים מפתחות על פני ארבע רמות של קינון תמלול כי ביד לוקח עשר דקות של עבודה מכנית טהורה, ועבודה מכנית שבוצעה על ידי בני אדם יש שיעור פגם אתה תטעה שם מפתח אתה תחמיץ את השדה אחד ש's מערך של אובייקטים ולא מערך של מחרוזות המחולל לא.
אבל הסיבה החזקה ביותר היא שדור עושה את הצורה גלוי לעין. הדבק תגובה לתוך ממיר ואתה רואה מיד דברים שאתה ' d הברקתם על קריאת JSON גולמי: זה metadata הוא למעשה אובייקט מקונן עמוק, זה tags לפעמים ריק, שחצי מהמפתחות ברשימה המעומדת שלך חסרים ברשומות מסוימות. הממשק שנוצר הוא סיכום של הנתונים 's המבנה האמיתי, וקריאתו היא לרוב הדרך המהירה ביותר להבין נקודת קצה שעשית 't לכתוב. I' השתמשתי בו כשלב תיעוד יותר מפעם אחת בממשקי API שהמסמכים שלהם היו שקר.
היכן זה מתאים לצד כלי הנתונים האחרים שלך: אם אתה 're בודק את המטען במקום להקליד אותו, ה פורמט JSON האם התחנה הראשונה הטובה יותר, ואם אתה 're משווה שתי תגובות כדי לראות מה השתנה בין גרסאות, ה JSON Diff עונה על כך ישירות.
כיצד בעצם עובדת הסקת סוג מ-JSON?
ל-JSON יש שישה סוגי ערכים, לכל RFC 8259: אובייקט, מערך, מחרוזת, מספר, true/false, ו null. TypeScript's טיפוסים פרימיטיביים ממפים על ארבעה מהם כמעט ישירות העבודה המעניינת היא כולה בשני האחרים.
פרימיטיבים הם טריוויאליים. ערך מחרוזת מרמז string. מספר מרמז number- שים לב של-JSON יש סוג מספרי אחד, אז אין ' אין מידע בנתונים שאומר לך אם 1 הוא מספר שלם או צף, ו-TypeScript עושה 't להבחין בכל מקרה. 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. עם הגרסה המוטבעת you'd להיות כתיבה Project['owner'] בכל מקום, שעובד אבל קורא רע.
מערכים הם המקום שבו ההחלטות האמיתיות חיות. סוג array's הוא האיחוד של סוגי האלמנטים שלו, אז [1, 2, 3] נותן number[] ו [1, "a"] נותן (number | string)[]. שימו לב לסוגריים באותו השני - בלעדיהם, number | string[] אומר משהו אחר לגמרי (מספר או מערך של מחרוזות), ומחוללים ששוכחים את זה פולטים קוד שמהדר אבל מתאר את הדבר הלא נכון.
מערכים ריקים הם מבוי סתום כנה. "tags": [] אומר לך שקיים מפתח ומחזיק מערך; זה לא אומר לך כלום על מה שנכנס בו. הפלט הנכון הוא unknown[], ואתה צריך לקרוא את זה בתור המחולל שמסרב לנחש ולא כתשובה מוגמרת. מלא את זה בעצמך מתיעוד, או מצא דוגמה שבה המערך נמצא 't ריק.
מדוע מערכים של אובייקטים מתמזגים במקום מאוגדים?
זוהי ההחלטה הבודדת שמפרידה בין מחולל you'd להשתמש מאחד you'd לנטוש לאחר חמש דקות.
שקול תגובה מעוטרת שבה הרשומות הן 't אחיד לחלוטין - כלומר, כל תגובה מעוטרת אמיתית:
{
"rows": [
{ "id": 1, "name": "Ada", "nickname": "The Countess" },
{ "id": 2, "name": "Grace" }
]
}
התייחסו לכל אלמנט באופן עצמאי ותקבלו איחוד של שני ממשקים: rows: (Row1 | Row2)[]. זהו מבחינה טכנית הקריאה המדויקת ביותר של המדגם, וזה חסר תועלת כל גישה ל row.nickname כעת דורש צמצום, כי TypeScript יכול' לא יודע איזה חבר באיגוד יש לך הרחב את זה לתגובה של חמישים רשומות עם מספר שדות אופציונליים ותקבל איחוד של עשרות ממשקים כמעט זהים אף אחד לא רוצה את זה.
הקריאה השימושית היא ששני האובייקטים הללו הם שני מופעים של ישות אחת, ו nickname האם שדה של גרייס יש 't:
interface Row {
id: number
name: string
nickname?: string
}
interface T {
rows: Row[]
}
That' הוא מיזוג: אסוף כל מפתח שנראה על פני כל האלמנטים, וסמן מפתח אופציונלי אם הוא ' נעדר מכל אחד מהם. זה תואם לאופן שבו הנתונים מופקים בפועל - טבלת מסד נתונים אחת, סדרתי אחד, כמה עמודות ניתנות לביטול - והוא מייצר סוגים שבהם אתה יכול להשתמש ללא טקס. שם רכיב המערך הוא גם יחיד, אז releases תשואות Release יותר מאשר Releases, כי releases: Releases[] קורא כמו באג גם כשהוא 't.
הפשרה אמיתית ושווה לציין בבירור: מיזוג מניח שהמערך הומוגני. אם יש לך מערך הטרוגני באמת - הזנה של אירועים בעלי צורות שונות, המופלים על ידי א type שדה - מיזוג משטח גרסאות שונות לממשק אחד שבו כמעט הכל אופציונלי. That' הוא הדגם הלא נכון, והוא ' הוא מקרה שבו עליך לקחת את הפלט שנוצר כנקודת התחלה ולכתוב ביד איחוד מופלה כהלכה. גנרטורים don' לא מכיר את התחום שלך. זה ממזג אובייקטים ומאחד את כל השאר, וזה נכון רוב הזמן ושגוי באופן שאתה יכול לזהות מיד.
האם 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 והמפתח אולי לא קיים על האובייקט בכלל. In B, המפתח תמיד קיים, והערך שלו עשוי להיות null. תחת strictNullChecks- איזה ה מדריך TypeScript ממליץ ועל מה אתה צריך - שניהם מאלצים אותך לטפל במקרה הנעדר, אבל הם מאלצים בדיקות שונות והם עושים סדרה אחרת. JSON.stringify משמיט undefined מאפיינים לחלוטין ופולט null עבור אפס, כך שהבחירה מתפשטת כל הדרך חזרה החוצה אל החוט.
התשובה הנכונה תלויה ב-API's התנהגות בפועל, שאף מחולל לא יכול לראות ממדגם אחד:
| התנהגות ה-API ו-#39; שלך | דגם נכון | למה |
|---|---|---|
| משמיט את המפתח כאשר יש ' אין ערך | key?: T |
המפתח באמת הוא ' שם; אופציונלי הוא מדויק |
תמיד שולח את המפתח, null כאשר ריק |
key: T | null |
המפתח תמיד קיים; ? יאפשר היעדרות בטעות |
| לא עקבי - לפעמים מושמט, לפעמים ריק | key?: T | null |
שני המקרים אמיתיים; לדגמן את שניהם |
שולח null רק על תגובות שגיאה |
גם לא - מדגמנים את השגיאה בנפרד | שדה שניתן לבטל מסתיר איחוד של צורות תגובה |
השורה האחרונה היא זו שכדאי להשהות עליה שדה שהולך בטל רק במקרים של כשל הוא אות שנקודת הקצה מחזירה שני דברים שונים לובשים צורה אחת, והתיקון הוא איחוד מופלה בשדה סטטוס, לא מאפיין שניתן לבטל. יצירת סוג משטח את הדפוס הזה; זה ' לא פותר את זה.
ברירת המחדל של הממיר היא key?: T מכיוון שהושמט-כאשר-נעדר היא המוסכמה הנפוצה יותר בממשקי ה-API של JSON I' עבדתי איתם, ומכיוון שהוא מורכב טוב יותר עם מיזוג המערך שתואר לעיל (מפתח חסר ברשומות מסוימות ומפתח ש-' null ברשומות מסוימות לקבל דגם באותו אופן). כבה את האפשרות ו null נשאר באיגוד במקום. no are a trick; בחר את זה שה-API שלך עושה בפועל.
מה לגבי מפתחות שהם 't מזהי TypeScript חוקיים?
מקשי אובייקט JSON הם מחרוזות שרירותיות. שמות מאפיינים של TypeScript בחשוף key: T מיקום הם לא - הם צריכים להיות מזהים חוקיים. כך "content-type", "2fa", "user.name", ו "" האם כל מפתחות JSON חוקיים שלא ניתן לכתוב ללא ציטוט בממשק.
התיקון הוא ציטוט, וזה ' לא פתרון - שמות נכסים מצוטטים הם TypeScript רגילים:
interface Headers {
"content-type": string
"2fa": boolean
class: string
}
ניתן לגשת למאפיינים אלה באמצעות סימון סוגר (headers["content-type"]), שהוא קצת יותר מילולי אבל לגמרי בטוח לסוג. שים לב ש class does't צריך ציטוט: מילים שמורות הן חוקיות לחלוטין כמו שמות נכסים, למרות שהם' אינם חוקיים כמזהים. ההגבלה חלה רק כאשר TypeScript מצפה למזהה - וזו הסיבה שאותה מילה אכן זקוקה לטיפול כאשר היא הופכת לממשק שם.
שמות ממשקים שנגזרו ממפתחות כאלה זקוקים ליותר עבודה מאשר ציטוט. 2fa PascalCases ל 2fa, אשר יכול't להתחיל מזהה, כך שהוא מקבל קידומת. שני אובייקטים מקוננים שונים שניהם תחת מפתחות בשם owner האם שניהם ירצו להיות Owner, אז השני הופך Owner2. אלו פרטים לא זוהרים, והם ' הם בדיוק הפרטים שמחליטים אם הפלט שנוצר מהדר או צריך חמש עשרה דקות של תיקון ידני לפני שהוא עושה. הבדיקה שאני מחזיק את הממיר אליה היא פשוטה: הדבק כל דבר חוקי, והפלט צריך להדר תחת strict ללא עריכות.
ממשקים או כינויי סוג?
גם הגנרטור פולט ההבדלים המעשיים צרים אך אמיתיים, ולבסיס הקוד שלך כנראה כבר יש דעה מקודדת בתצורת המוך שלו.
interface User {} תומך במיזוג הצהרות - הכריז על אותו שם ממשק פעמיים ו-TypeScript משלב אותם. That's חיוני להגדלת סוגים מספריות שאתה עושה 't control, ואקדח רגל בכל מקום אחר, שכן שתי הצהרות לא קשורות באותו שם מתמזגות בשקט במקום לטעות. ממשקים תומכים גם הם extends, שמייצר הודעות שגיאה מעט טובות יותר מסוגי צומת כאשר אילוץ נכשל.
type User = {} can't מיזוג, שהוא בדרך כלל תכונה, וזה's נדרש לכל דבר שהוא't צורת אובייקט: איגודים, tuples, טיפוסים ממופים, טיפוסים מותנים. root that is't אובייקט JSON - מערך של מספרים, מחרוזת חשופה - יכול לבוא לידי ביטוי רק ככינוי, אז type Nums = number[] הוא מה שאתה מקבל ללא קשר להגדרה.
עבור סוגי API שנוצרו אני נוטה אליהם interface, בעיקר בגלל שהודעות השגיאה טובות יותר באופן שולי ומכיוון שסיכון המיזוג הוא תיאורטי כאשר כל שם חי בקובץ אחד שנוצר. אבל זה קרוב להיפוך מטבע, והעקביות עם הקוד שמסביב חשובה יותר מהיתרונות. אם יש ל-ESLint config שלך @typescript-eslint/consistent-type-definitions הגדר כך או כך, התאם אותו והפסק לחשוב עליו.
במה זה שונה מ-JSON Schema ל-TypeScript?
אלה פותרים בעיות שונות באמת וזה ' שווה לדייק, כי "JSON ל-TypeScript" ו-"JSON Schema ל-TypeScript" הם מילה אחת בנפרד ולעתים קרובות מבולבלים.
JSON ל-TypeScript הוא הסקה מדוגמה. קלט: ערך המחולל מתבונן במה' נמצא שם ומכליל. הוא לא יכול לדעת אם נדרש שדה, אם מחרוזת מוגבלת ל-enum, אם למספר יש מינימום, או אם הדגימה האחת שהדבקת מייצגת. It's אינדוקציה מתצפית אחת, עם כל מה שמשתמע מכך.
JSON Schema to TypeScript הוא תרגום מהצהרה. קלט: א סכימת JSON מסמך, שכבר מציין סוגים, required מערכים, enums, פורמטים, ואילוצים. the generator is't guessing - it's transcripting a existing contract into TypeScript syntax. required מפות לנכסים לא אופציונליים; א enum מפות לאיחוד מילולי של מחרוזת; oneOf מפות לסוג איחוד.
הכלל בא ישירות: אם קיימת סכימה, השתמש בה. סכימת JSON, מפרט OpenAPI, א .proto קובץ, או סכימת GraphQL סמכותית באופן שתגובת דגימה לעולם אינה. Inference הוא מה שאתה מגיע אליו כאשר לא קיימת סכימה - נקודת קצה פנימית לא מתועדת, API של צד שלישי שהמסמכים שלו מיושנים, פורמט קובץ תצורה שגדל באופן אורגני, מתקן שאתה 're כתיבת מבחנים נגדו, אשר, למען ההגינות, מתאר חלק גדול מה-JSON שכל אחד מאיתנו עוסק בו בפועל.
There's נתיב ביניים ראוי להזכיר: השתמש בהסקת מסקנות אתחול, ואז לשמור ביד. צור את הממשק מתגובה אמיתית כדי לקבל את הצורה ואת שמות השדות הנכונים, ואז ערוך אותו - הדק א string לאיחוד מילולי שבו אתה יודע את הערכים המותרים, תקן א unknown[] המדגם נותר ריק, לפצל ממשק ממוזג לאיחוד מופלה תקין המחולל עושה את מכני 90% ואתה מיישם את הידע תחום זה מבחינה מבנית לא יכול להיות.
איפה הסקת מסקנות טועה?
רשימה קצרה וכנה כל אחד מאלה הוא מגבלה של הגישה, לא באג בכלי מסוים, והכרתם היא ההבדל בין שימוש טוב בסוגים שנוצרו לבין צריבה מהם.
דגימות בודדות קובעות פחות את הסוג. שדה ש's number בדוגמה שלך עשוי להיות null ב-5% מהרשומות. שדה ש-'s הקיימים בכל שלוש הרשומות שהדבקת עשוי להיות אופציונלי על פני מערך הנתונים המלא. מסקנות מדווחות על מה שהיא ראתה. הדבק יותר רשומות - באופן אידיאלי דף אמיתי של תוצאות ולא אובייקט אחד שנבחר ביד - והאופציות מקבלים מדויקות יותר באופן משמעותי.
מיתרים מסתירים את הטיפוסים האמיתיים שלהם. חותמות זמן ISO, UUIDs, כתובות URL וכתובות דואל הם כולם רק string למנתח JSON. "2026-07-16T09:00:00Z" האם מבחינה סמנטית תאריך; שום דבר בנתונים לא אומר זאת. אם לבסיס הקוד שלך יש מותג ISODateString הקלד, אתה're מחליף אותו ביד.
מספרים מאבדים הבחנות מדויקות. JSON's סוג מספר בודד פירושו מזהה ש-'s מספר שלם של 64 סיביות בשרת מגיע כמספר JavaScript ואולי כבר איבד דיוק לפני שהמחולל שלך אי פעם ראה אותו - Number.MAX_SAFE_INTEGER הוא בערך 9×10¹5, וטוויטר נודע כי בדרך הקשה.If שלך API שולח מספרים שלמים גדולים כמחרוזות, that's למה, ואת שנוצר string נכון.
ערכים מילוליים נראים כמו הסוגים הכלליים שלהם. "status": "active" מסיק string, לא "active" | "archived" | "pending". הסוג הצר יותר שימושי יותר ואף דגימה לא יכולה להוכיח זאת. זוהי העריכה הידנית הנפוצה ביותר שאני עושה לפלט שנוצר.
מיכלים ריקים לא אומרים כלום. [] נותן unknown[] ו {} נותן ממשק ריק שניהם הגנרטור להיות כנה.
כל זה לא הופך את ההסקה לא בטוחה - זה הופך אותה ל-a טיוטה. זרימת העבודה שעובדת היא: צור, קרא את הפלט בעיון, תקן את ארבעת או חמשת הדברים שאתה יודע שהדגימה יכלה ' לא אומר, להתחייב. That's עדיין בסדר גודל מהיר ומדויק יותר מתמלול שלושים מפתחות ביד, שזו האלטרנטיבה בפועל.
האם JSON שלי מקבל העלה בכל מקום?
לא, וזו קטגוריה של כלי שבו השאלה ראויה לתשובה אמיתית ולא לתג.
תחשוב על מה 's ב-JSON you'd הדבק לתוך מחולל סוגים. It's תגובת API, מה שאומר שהיא מכילה באופן סביר אסימון נושא, מזהה הפעלה, אימייל ללקוח, מזהה משתמש פנימי, שכבת תמחור, סוד webhook. That's לא היפותטי - it's המקרה המודאלי, כי כל העניין הוא שתפסת א אמיתי תגובה להקליד נגד.
כל ממיר בצד השרת מקבל בהכרח את המטען הזה. זה אולי לא רושם את זה, וזה כנראה 't, אבל אתה ' מרחיבים אמון שאתה עושה ' לא צריך להרחיב, ובהתאם לנתונים ייתכן שאתה יוצר בעיית תאימות למשימה שאין לה עסק לגעת ברשת בכלל.
הסקת סוג היא חישוב טהור על ערך מנותח הוא לא צריך רשת, אין חשבון, אין אחסון הממיר ב-Toolz.dev הוא כמה מאות שורות של TypeScript ללא תלות הפועלות בכרטיסייה שלך; המטען הוא מחרוזת JavaScript בדפדפן שלך's זיכרון והוא נשאר שם אתה יכול לאמת זאת כמו שאתה'd לאמת כל טענה כזו - פתח את לשונית הרשת ולחץ על צור, או כבה את ה-wifi שלך וראה אותו ממשיך לעבוד זהו אותו עיקרון מאחורי כל כלי באתר, ו-I' כתב על למה זה חשוב יותר באופן רחב מדוע כלים מבוססי דפדפן מנצחים את הכלים בצד השרת עבור נתונים רגישים.
דוגמה מעובדת
Here'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 הפך לאופציונלי מכיוון שהמהדורה השנייה עשתה ' אין אחד. retiredAt הפך לאופציונלי מכיוון שהערך היחיד שנצפה בו היה ריק.
ועכשיו קרא את מה שאתה 'd לתקן. retiredAt?: null האם המחולל 's דיווח כנה שהוא מעולם לא ראה ערך שאינו ריק, והוא 's חסר תועלת כסוג - you'd לשנות אותו ל retiredAt?: string כי אתה יודע את זה ' הוא חותמת זמן כאשר נוכח. העריכה הבודדת הזו היא כל השיעור: המחולל קיבל את מבנה שבעת המקשים, הקינון, מיזוג המערך והאופציונליות ממש בהדבקה אחת, והשאיר אותך עם ההחלטה האחת שדרשה לדעת מה השדה אומר.
שָׁוא
כיצד אוכל להמיר JSON לממשק TypeScript?
הדבק את ה-JSON שלך בממיר, הגדר את שם סוג השורש לכל מה שנקרא המשאב, ולחץ על צור. זה מסיק את הסוג של כל מקש, שולף אובייקטים מקוננים החוצה לממשקים בעלי שם משלהם, ממזג מערכים של אובייקטים לסוג אלמנט בודד ומוציא קוד שאתה יכול להעתיק ישר לתוך א .ts קוֹבֶץ. There's אין הרשמה ואין העלאה - ההסקה פועלת בדפדפן שלך.
מה קורה למערכים של אובייקטים?
They're התמזגו לממשק אחד המתאר אלמנט בודד, והמאפיין מוקלד כמערך שלו. כל מפתח שמופיע בחלק מחברי המערך אך לא באחרים הופך לאופציונלי. זה תואם את האופן שבו נתונים מעומדים אמיתיים מתנהגים, כאשר רשומות מגיעות מטבלה אחת וחלק מהעמודות ניתנות לביטול. המקרה היחיד שהוא מטפל בו בצורה גרועה הוא מערך הטרוגני באמת של סוגי אירועים שונים, שעליך להמיר ביד לאיגוד מופלה.
האם null צריך להפוך למפתח אופציונלי או לאיחוד עם null?
זה תלוי אם ה-API שלך משמיט שדות נעדרים או שולח אותם כ-null. אם זה משמיט אותם, key?: T מדויק אם המפתח קיים תמיד ולפעמים ריק, key: T | null מדויק, ומשתמש ? יאפשר בטעות למפתח להיות חסר. ברירת המחדל של הממיר היא אופציונלית ומאפשרת לך לעבור, מכיוון שהשניים מסודרים אחרת - JSON.stringify מפיל מאפיינים לא מוגדרים אך פולט מאפיינים אפסיים.
האם זה יכול להסיק סוגים מדויקים ממדגם JSON בודד?
זה מסיק סוגים מדויקים עבור המדגם הזה, שהוא't אותו דבר. שדה ש-'s מספר ברשומה האחת שלך עשוי להיות ריק באחרים; שדה הקיים בכל שלוש הרשומות שהדבקת עשוי להיות אופציונלי על פני מערך הנתונים המלא. השתמש במטען מייצג עם מספר רשומות ולא באובייקט אחד שנבחר ביד, והתייחס לפלט כאל טיוטה שנבדקה ולא כחוזה מוגמר.
מה 's ההבדל בין JSON ל-TypeScript ו-JSON Schema ל-TypeScript?
כלי זה מסיק סוגים מערך לדוגמה; JSON Schema ל-TypeScript מתרגם סכימה פורמלית שכבר מצהירה על טיפוסים, שדות נדרשים ו-enums. הסכימה היא סמכותית והסקת מסקנות היא ניחוש, אז אם יש לך סכימת JSON, מפרט OpenAPI או סכימת GraphQL, השתמש בה. מסקנות מיועדות למקרה הנפוץ מאוד שבו לא קיימת סכימה וכל מה שיש לך הוא גוף תגובה.
כיצד הוא מטפל במפתחות שהם 't מזהים חוקיים?
מפתחות עם מקפים, נקודות, רווחים או ספרות מובילות מצוטטים בפלט, אז "content-type" הופך "content-type": string. That's typeScript חוקי, גישה עם סימון סוגר. מילים שמורות כמו class don't צריך ציטוט כשמות מאפיינים שמות ממשקים שנגזרו ממפתחות כאלה הם PascalCased וקידומת אם הם'd מתחילים בספרה, ושמות מתנגשים מקבלים סיומת מספרית כך שהפלט תמיד מהדר.
האם עלי ליצור ממשקים או כינויי סוג?
התאם את כל מה שבסיס הקוד שלך כבר עושה - זו בעיקר שאלת עקביות. ממשקים תומכים במיזוג הצהרות ו extends, ולתת הודעות שגיאה ברורות יותר באופן שולי. Type aliases can't merge, שבדרך כלל רצוי, ונדרשים לכל דבר שהוא't צורת אובייקט. שורש ש-'s מערך או פרימיטיבי נפלט ככינוי בכל מקרה, מכיוון ש-there's אין אובייקט להכריז עליו ממשק.
האם ה-JSON שלי מועלה לשרת?
מס 'מנוע ההסקה כולו פועל כ - JavaScript בדפדפן שלך, ללא שיחות רשת, ללא רישום, וללא אחסון זה חשוב כאן יותר מאשר עבור רוב הכלים, כי JSON you' d הדבק לתוך מחולל סוג הוא בדרך כלל תגובת API אמיתית המכילה אסימונים, רשומות לקוחות או מזהים פנימיים פתח את הכרטיסייה שלך ברשת תוך כדי יצירה, או ניתוק מהאינטרנט - זה ממשיך לעבוד.
כלים קשורים: פורמט JSON לבדיקת המטען תחילה, JSON ל-YAML ו JSON ל-XML להמרת פורמט, ו JSON Diff לאיתור מה השתנה בין שתי תגובות. קריאה נוספת: המדריך האולטימטיבי לכלי JSON ו המדריך לכלי קידוד של developer's.



