توليد أنواع TypeScript من Schema
الخلاصة: json-schema-to-typescript وquicktype يصدران ملف .d.ts بأمر واحد، لكنه مسودة: الاختيارية والاتحادات وadditionalProperties والتواريخ تحتاج إلى مراجعة بشرية.

الخلاصة: ولّد بـjson-schema-to-typescript أو quicktype، ثم أصلح يدويًا الاختيارية والاتحادات وadditionalProperties والتواريخ؛ وأبقِ Schema مصدرًا والأنواع مخرجات، مفروضًا بخطوة types:check في CI.
أمر واحد يمنحك ملفًا. لكنه لا يمنحك أنواعًا صحيحة.
أمر واحد
npx json-schema-to-typescript schema.json > src/types/order.d.ts
يتعامل مع $ref وallOf وoneOf وanyOf، ويحوّل description إلى JSDoc. وللتوليد الدفعي مع لافتة وstyle مختار، استدعِ واجهة Node API من build-types.mjs صغير.
quicktype هو البديل، ويستطيع أيضًا استنتاج مخطط من JSON عيني:
npx quicktype -s schema schema.json -o order.ts --lang typescript
أربعة أشياء تُصلح بعد التوليد
الاختيارية. أي شيء خارج required يصير ?. لكن «دائم الحضور، وقد يكون null» و«قد يكون غائبًا» عقدان مختلفان:
interface Order {
remark?: string | null; // may be missing
status: string;
}
إن كان الخادم يرسل remark: null دائمًا، فاحذف ? وكفّ عن جعل كل مستهلك يحرس undefined.
الاتحادات غير المميزة. oneOf يصير عادةً A | B، ولا يستطيع TypeScript تضييقه دون حقل حرفي. أضف خاصية type ثابتة إلى كل فرع في Schema.
additionalProperties. حذفه أو كونه true ينتج أحيانًا [k: string]: unknown وأحيانًا any. الأول آمن، والثاني يعطّل فحص الأنواع للكائن كله بصمت.
تنسيقات التاريخ. format: "date-time" سلسلة على السلك. وتعيينه إلى Date يعني أنه يجب عليك أيضًا إلغاء التسلسل، وإلا بقيت القيمة وقت التشغيل سلسلة بينما يكذب النوع.
أبقِ الاتجاه ثابتًا
Schema هو المصدر والأنواع مخرجات. توليد Schema من أنواع مكتوبة يدويًا مقبول فقط إن كان إعادة التوليد إلزامية ومفروضة في CI — وإلا انحرف الطرفان ولم يعرف أحد أيهما المرجع.
ضعه في خط الأنابيب
"types:gen": "json2ts -i schemas/ -o src/types/generated",
"types:check": "pnpm types:gen && git diff --exit-code src/types/generated"
types:check هو الجزء المهم: غيّر Schema دون إعادة توليد فيفشل CI.
الأسئلة الشائعة
لماذا يحتوي الملف المولد على any؟
يأتي عادةً من additionalProperties كونه true أو محذوفًا، وهو ما يعينه بعض المولدات إلى [k: string]: any. استبدله بـunknown أو بحقول صريحة، لأن any يعطّل فحص الأنواع للكائن كله.
كيف أصلح الحقول الاختيارية بعد التوليد؟
أي شيء خارج required يصير ?، لكن «دائم الحضور، وقد يكون null» يختلف عن «قد يكون غائبًا». إن كان الخادم يرسل الحقل دائمًا، فاحذف ?.
هل يجب أن يفشل CI عندما ينحرف Schema والأنواع؟
نعم. سكربت types:check الذي يعيد التوليد ويشغّل git diff --exit-code يفشل البناء بمجرد تغيّر Schema دون إعادة توليد.