Sinh kiểu TypeScript từ một Schema
Kết luận: json-schema-to-typescript và quicktype phát ra tệp .d.ts chỉ với một lệnh, nhưng đó mới là bản nháp: tính tùy chọn, union, additionalProperties và ngày tháng vẫn cần con người rà lại.

Tóm lại: hãy sinh bằng json-schema-to-typescript hoặc quicktype, rồi sửa tay tính tùy chọn, union, additionalProperties và ngày tháng; giữ Schema là nguồn và kiểu là đầu ra, được thực thi bằng một bước types:check trong CI.
Một lệnh cho bạn một tệp. Nó không cho bạn kiểu đúng.
Lệnh một dòng
npx json-schema-to-typescript schema.json > src/types/order.d.ts
Nó xử lý $ref, allOf, oneOf, anyOf, và chuyển description thành JSDoc. Để sinh hàng loạt với banner và style đã chọn, hãy gọi Node API của nó từ một tệp build-types.mjs nhỏ.
quicktype là lựa chọn thay thế, và cũng có thể suy ra schema từ JSON mẫu:
npx quicktype -s schema schema.json -o order.ts --lang typescript
Bốn điểm cần sửa sau đó
Tính tùy chọn. Bất cứ gì ngoài required đều thành ?. Nhưng "luôn có mặt, có thể null" và "có thể vắng mặt" là hai hợp đồng khác nhau:
interface Order {
remark?: string | null; // may be missing
status: string;
}
Nếu backend luôn gửi remark: null, hãy bỏ ? và đừng bắt mọi bên tiêu thụ phải phòng vệ với undefined nữa.
Union không có trường phân biệt. oneOf thường thành A | B, mà TypeScript không thu hẹp được nếu thiếu một trường literal. Hãy thêm thuộc tính type cố định cho mỗi nhánh trong Schema.
additionalProperties. Bị bỏ qua hoặc true đôi khi tạo ra [k: string]: unknown, đôi khi any. Cái đầu an toàn, cái sau âm thầm tắt kiểm tra kiểu cho toàn bộ đối tượng.
Định dạng ngày. format: "date-time" là chuỗi trên đường truyền. Ánh xạ nó thành Date nghĩa là bạn cũng phải giải tuần tự hóa, nếu không giá trị lúc chạy vẫn là chuỗi trong khi kiểu nói dối.
Giữ hướng cố định
Schema là nguồn, kiểu là đầu ra. Sinh Schema từ kiểu viết tay chỉ chấp nhận được nếu việc sinh lại là bắt buộc và được thực thi trong CI — nếu không hai phía sẽ lệch nhau và không ai biết phía nào là chuẩn.
Đưa vào pipeline
"types:gen": "json2ts -i schemas/ -o src/types/generated",
"types:check": "pnpm types:gen && git diff --exit-code src/types/generated"
types:check mới là phần quan trọng: thay đổi Schema mà không sinh lại thì CI thất bại.
Câu hỏi thường gặp
Vì sao tệp được sinh có chứa any?
Nó thường đến từ additionalProperties bằng true hoặc bị bỏ qua, mà một số trình sinh ánh xạ thành [k: string]: any. Hãy thay bằng unknown hoặc các trường tường minh, vì any tắt kiểm tra kiểu cho toàn bộ đối tượng.
Sau khi sinh, tôi nên sửa trường tùy chọn thế nào?
Bất cứ gì ngoài required đều thành ?, nhưng "luôn có mặt, có thể null" khác với "có thể vắng mặt". Nếu backend luôn gửi trường đó, hãy bỏ ?.
CI có nên thất bại khi Schema và kiểu lệch nhau không?
Có. Một script types:check sinh lại rồi chạy git diff --exit-code sẽ làm build thất bại ngay khi Schema thay đổi mà không sinh lại.