§

Mẫu JSON

Đang chuẩn bị bộ suy luận kiểu…
§

Interface TypeScript

TypeScript

Các nhóm TypeScript tại Việt Nam gặp vấn đề này sớm. Các SDK lớn có client được gõ kiểu (Stripe, Twilio, AWS), nhưng các dịch vụ nội bộ và payload webhook của bên thứ ba hiếm khi có, vì vậy quy trình thông thường là: chụp phản hồi thực trong bảng network, dán vào đây, đặt tên gốc theo endpoint và sao chép đầu ra vào thư mục types của dự án. Từ đó, strict mode bắt các sự không khớp mà tài liệu quên đề cập. Bộ suy luận chạy hoàn toàn trong trình duyệt, vì vậy payload từ API staging, body webhook đã ký và các endpoint chưa phát hành không bao giờ đến dịch vụ được lưu trữ.

Cách suy luận JSON-to-TypeScript hoạt động

Suy luận là một lượt duyệt duy nhất trên cây JSON đã phân tích. Công cụ đọc từng giá trị, chọn kiểu TypeScript cho nó rồi viết ra một interface cho mỗi đối tượng nó tìm thấy.

  1. Phân tích mẫu JSON bằng trình phân tích cú pháp gốc của trình duyệt và từ chối đầu vào bị lỗi định dạng với gợi ý dòng/cột.
  2. Xác định kiểu TypeScript cho mỗi giá trị — string, number, boolean, null, mảng hay đối tượng lồng nhau.
  3. Đặt tên interface cho mỗi đối tượng lồng nhau dựa trên khóa thuộc tính cha (vì vậy user.address trở thành interface Address).
  4. Hợp nhất các kiểu mục trên mỗi mảng để danh sách {id: 1}{id: 2, label: "x"} tạo ra union với các trường optional đúng.
  5. Áp dụng tùy chọn của bạn (interface vs. type, readonly, optional-nullable) và phát ra khai báo theo thứ tự phụ thuộc để file biên dịch mà không có forward reference.

Tại sao tạo kiểu TypeScript từ JSON?

  • Hầu hết các lỗi hình dạng có thể bắt được tại thời điểm biên dịch nếu kiểu phản hồi được viết ra. Suy luận interface từ payload thực tế viết phần lớn nó cho bạn, và chế độ `strict` bắt các trường mà tài liệu quên đề cập.
  • Kết hợp các interface được suy luận với trình xác thực runtime như Zod hay io-ts cho cùng một hình dạng hai công việc: autocomplete trong trình soạn thảo trong quá trình phát triển và 400 ở edge khi production gửi thứ gì đó bất ngờ.
  • Language server của TypeScript chỉ hiển thị các trường nó biết. Khi bạn import interface được suy luận, autocomplete hoạt động ngay khi bạn gõ dấu chấm — không còn cast `as any` trên phản hồi và grep bực bội trên repo.
  • Nếu bạn sắp viết đặc tả OpenAPI, interface được suy luận là bản nháp đầu nhanh của schema phản hồi. Bạn vẫn muốn ví dụ và ràng buộc viết tay, nhưng tên thuộc tính và kiểu đã đúng.

Ứng dụng phổ biến

Suy luận hữu ích nhất khi payload thực tồn tại nhưng schema thì không.

  • Gõ kiểu payload webhook bên thứ ba từ Stripe, GitHub hay Twilio trước khi viết handler.
  • Khởi động kiểu cho REST API nội bộ để nhóm frontend có thể bắt đầu code với nó vào cùng ngày backend ra mắt.
  • Tạo điểm khởi đầu cho schema Zod, io-ts hay Valibot từ phản hồi API quan sát được.

Đầu ra trông như thế nào?

Cho một tài liệu JSON mẫu và tên gốc, bộ tạo tạo ra cây interface, một cho mỗi đối tượng lồng nhau. Với đầu vào bên dưới với tên gốc User:

Dán {"id":1,"name":"Alice","tags":["a","b"],"address":{"city":"Paris"}} với tên gốc User và bộ tạo tạo ra:

export interface User {
  id: number;
  name: string;
  tags: string[];
  address: Address;
}

export interface Address {
  city: string;
}
Lưu ý rằng address được thăng lên interface được đặt tên riêng — đó là đầu ra theo thứ tự phụ thuộc. Cùng JSON với kiểu khai báo type thay thế sẽ phát ra export type User = {...}; với nút gạt readonly được bật, mọi thuộc tính đều có modifier readonly.

Tùy chọn bộ tạo

Kiểu khai báo

Chọn interface (thành ngữ TypeScript chuẩn cho hình dạng đối tượng) hay type (tiện lợi nếu bạn sẽ cần mapped type, conditional type hay intersection sau này). Cả hai đều tạo ra hành vi runtime giống hệt nhau; sự lựa chọn là sở thích phong cách code.

Trường optional nullable

Khi giá trị mẫu là null, kiểu của trường trở thành T | null. Bật tùy chọn này cũng thêm modifier ? để trường là optional ở phía TypeScript — hữu ích khi API đôi khi bỏ qua khóa hoàn toàn thay vì trả về null.

Modifier Readonly

Thêm readonly vào mọi khai báo thuộc tính để interface được phát ra khớp với mô hình dữ liệu bất biến. Tiện lợi cho các Redux state slice, phản hồi API bị đóng băng hay bất cứ nơi nào bạn muốn compiler gắn cờ sự biến đổi vô tình.

Có hỗ trợ đối tượng và mảng lồng nhau không?

Có. Mọi đối tượng lồng nhau trở thành interface được đặt tên dựa trên khóa thuộc tính cha, và mảng suy luận kiểu mục từ nội dung của chúng. Mảng đối tượng nhận interface cho mỗi hình dạng đối tượng, với union type khi các hình dạng không đồng nhất.

Các trường optional được suy luận như thế nào?

Bật nút gạt "Đánh dấu trường null-able là optional" và bất kỳ trường nào có giá trị mẫu là null sẽ nhận modifier ? trên khóa cộng với | null trong kiểu. Không có nút gạt, trường vẫn bắt buộc và kiểu chỉ là T | null.

Có hỗ trợ discriminated union không?

Các union type cơ bản xuất hiện khi mảng chứa các mục có hình dạng hỗn hợp hay khi trường mang cả giá trị lẫn null. Suy luận discriminated union đầy đủ (chọn type hay kind làm tag và tách variant) cần nhiều mẫu — điều đó đã được lên kế hoạch nhưng không có trong build hiện tại.

Có thể suy luận kiểu từ nhiều mẫu JSON không?

Chưa — bộ suy luận hiện tại đọc một mẫu mỗi lần. Nếu bạn có hai payload nên chia sẻ một interface (chẳng hạn endpoint danh sách và endpoint mục đơn), cách giải quyết thực tế là hợp nhất chúng thành một mảng, tạo từ đó rồi đổi tên các union type kết quả. Suy luận đa mẫu nằm trong lộ trình vì đó là cách duy nhất để phát hiện các trường có trong một phản hồi và vắng mặt trong phản hồi khác.

Dán payload, đặt tên gốc, sao chép interface. Toàn bộ pipeline chạy trong trình duyệt, vì vậy API chưa phát hành hay body webhook đã ký vẫn trên máy của bạn.