Schema から TypeScript 型を生成する
要点:json-schema-to-typescript と quicktype は一コマンドで .d.ts を出力しますが、それは草案です。任意性、ユニオン、additionalProperties、日付には人の手が必要です。

要点:json-schema-to-typescript か quicktype で生成し、その後で任意性、ユニオン、additionalProperties、日付を手で直しましょう。Schema を源、型を出力として保ち、CI の types:check ステップで強制します。
一コマンドでファイルが得られます。正しい型は得られません。
ワンライナー
npx json-schema-to-typescript schema.json > src/types/order.d.ts
$ref、allOf、oneOf、anyOf を扱い、description を JSDoc に変換します。バナーと選んだ style でバッチ生成するには、小さな build-types.mjs から Node API を呼びます。
quicktype は代替で、サンプル JSON からスキーマを推論もできます。
npx quicktype -s schema schema.json -o order.ts --lang typescript
後から直す四つ
任意性。 required にないものは ? になります。しかし「常に存在し、null かもしれない」と「存在しないことがある」は別の契約です。
interface Order {
remark?: string | null; // 欠落しうる
status: string;
}
バックエンドが常に remark: null を送るなら、? を外し、すべての消費者に undefined のガードを書かせるのをやめます。
判別なしユニオン。 oneOf は通常 A | B になり、TypeScript はリテラルフィールドなしに絞り込めません。Schema の各分岐に固定の type プロパティを足します。
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 が失敗します。
FAQ
生成されたファイルに any が含まれるのはなぜですか?
通常は additionalProperties が true か省略されているためで、一部の生成器が [k: string]: any にマップします。unknown か明示的なフィールドに置き換えてください。any はオブジェクト全体の型チェックを無効にします。
生成後の任意フィールドはどう直すべきですか?
required 外のものは ? になりますが、「常に存在し、null かもしれない」は「存在しないことがある」と異なります。バックエンドが常にそのフィールドを送るなら ? を外します。
Schema と型がずれたら CI を失敗させるべきですか?
はい。再生成して git diff --exit-code を実行する types:check スクリプトは、Schema を変えて再生成しなければビルドを失敗させます。