Sinh kiểu TypeScript từ JSON: luật suy kiểu, chỗ cần sửa tay và giới hạn của cách làm này
Dán một khối JSON, công cụ suy ra kiểu cho từng trường rồi sinh khai báo TypeScript, tự tách các đối tượng lồng nhau thành khai báo riêng. Bạn chọn kiểu khai báo là interface hay type, đặt tên cho khai báo gốc và bật chế độ đánh dấu mọi thuộc tính là tùy chọn nếu cần.
Tính năng nổi bật
- Suy kiểu cho từng trường từ giá trị mẫu và sinh khai báo TypeScript có từ khóa xuất
- Công tắc chọn giữa khai báo dạng interface và khai báo dạng type
- Ô đặt tên cho khai báo gốc, mặc định là Root
- Công tắc đánh dấu toàn bộ thuộc tính là tùy chọn bằng dấu chấm hỏi
- Tự tách mỗi đối tượng lồng nhau thành một khai báo riêng, tên lấy từ tên khóa viết hoa chữ đầu
- Suy kiểu mảng: mảng đồng nhất thành kiểu phần tử kèm dấu ngoặc vuông, mảng hỗn hợp thành hợp kiểu
- Mảng rỗng được suy thành mảng kiểu chưa biết thay vì đoán bừa
- Có sẵn nút nạp một khối JSON mẫu để bạn thử ngay, và nút sao chép kết quả
Vì sao gõ tay kiểu cho một phản hồi giao diện lập trình lại tốn thời gian
Một phản hồi giao diện lập trình thật thường có ba tới bốn tầng lồng nhau và hàng chục trường. Gõ tay toàn bộ khai báo kiểu cho nó là công việc thuần cơ học, mất nửa tiếng, và trong nửa tiếng đó bạn sẽ gõ sai ít nhất một tên trường. Cái sai đó không lộ ra ngay: trình biên dịch không thể biết máy chủ trả về tên nào, nó chỉ biết những gì bạn khai báo, nên mã vẫn dịch được và lỗi chỉ hiện ra khi chạy dưới dạng một giá trị không xác định. Dán thẳng phản hồi thật vào đây tránh được hoàn toàn nhóm lỗi đó, vì tên trường được lấy nguyên văn từ dữ liệu chứ không qua tay bạn. Đây cũng là lý do nên dán phản hồi thật lấy từ tab mạng của trình duyệt chứ đừng dán ví dụ chép từ tài liệu, vì tài liệu và thực tế không phải lúc nào cũng khớp.
Lợi ích khi sử dụng
- Tên trường lấy nguyên văn từ dữ liệu nên không còn lỗi gõ sai tên
- Cấu trúc lồng nhau được tách sẵn thành từng khai báo riêng, dễ tái sử dụng
- Có ngay khung kiểu để bắt đầu, việc còn lại chỉ là tinh chỉnh chứ không phải viết từ đầu
- Hai chế độ interface và type để khớp với quy ước sẵn có của dự án
- Dữ liệu phản hồi không rời khỏi trình duyệt nên dùng được với giao diện lập trình nội bộ
Cách sinh khai báo kiểu từ một phản hồi
- 1Mở tab mạng trong công cụ dành cho lập trình viên, chép nguyên phần thân phản hồi thật của lời gọi bạn quan tâm.
- 2Dán vào ô bên trái, đặt tên cho khai báo gốc theo tên tài nguyên chứ đừng để nguyên tên mặc định.
- 3Chọn interface hay type, và cân nhắc bật chế độ thuộc tính tùy chọn nếu bạn chưa chắc trường nào luôn có mặt.
- 4Bấm chuyển đổi rồi đọc kết quả từ dưới lên, vì khai báo gốc nằm ở cuối và các khai báo con nằm phía trên.
- 5Sửa tay ba nhóm: trường có giá trị rỗng, trường tùy chọn, và tên khai báo con bị trùng hoặc chưa đúng quy ước.
Luật suy kiểu mà công cụ áp dụng
Với giá trị nguyên thủy, kiểu được lấy trực tiếp từ kiểu thời gian chạy của giá trị, nên chuỗi thành string, số thành number, đúng sai thành boolean. Giá trị rỗng được suy thành đúng chữ null chứ không ghép thành hợp kiểu với kiểu khác, đây là chỗ bạn gần như luôn phải sửa tay. Với mảng, công cụ duyệt từng phần tử và gom lại tập kiểu tìm được: nếu mọi phần tử cùng kiểu thì kết quả là kiểu đó kèm dấu ngoặc vuông, nếu có nhiều kiểu khác nhau thì kết quả là hợp kiểu đặt trong ngoặc tròn rồi mới thêm dấu ngoặc vuông, còn mảng rỗng thì thành mảng kiểu chưa biết vì không có gì để suy. Với đối tượng lồng, công cụ sinh một khai báo riêng và đặt tên bằng cách viết hoa chữ cái đầu của tên khóa, sau đó trường cha nhận chính tên khai báo đó làm kiểu. Cuối cùng, các khai báo được ghép lại theo thứ tự đảo ngược so với thứ tự tạo, nên khai báo con hiện lên trước và khai báo gốc nằm cuối cùng.
Suy kiểu từ một mẫu dữ liệu là suy đoán, không phải hợp đồng
Đây là giới hạn cơ bản của mọi công cụ loại này, không riêng công cụ ở đây. Kiểu sinh ra mô tả đúng một mẫu dữ liệu bạn vừa dán, chứ không mô tả tất cả những gì máy chủ có thể trả về. Ba trường hợp hay gây rắc rối. Thứ nhất là trường chỉ xuất hiện trong một số bản ghi, ví dụ đơn hàng có mã giảm giá thì mới có trường giảm giá; mẫu của bạn không có nó thì kiểu cũng không có, và về sau ai đó truy cập trường ấy sẽ bị trình biên dịch chặn dù dữ liệu thật vẫn có. Thứ hai là trường có thể nhận giá trị rỗng nhưng trong mẫu lại đang có giá trị, khi đó kiểu sinh ra bỏ mất khả năng rỗng và mã của bạn sẽ vỡ lúc chạy thật. Thứ ba là khi mảng ở tầng gốc, công cụ chỉ đọc phần tử đầu tiên để dựng kiểu, nên nếu các phần tử khác nhau về cấu trúc thì phần khác biệt không được phản ánh. Nếu giao diện lập trình có sẵn đặc tả chính thức, hãy sinh kiểu từ đặc tả đó thay vì suy từ mẫu.
Trường tùy chọn và trường có thể rỗng là hai chuyện khác nhau
Trong TypeScript, đánh dấu một thuộc tính bằng dấu chấm hỏi nghĩa là thuộc tính đó có thể không tồn tại trong đối tượng. Còn khai báo kiểu là hợp kiểu với null nghĩa là thuộc tính luôn tồn tại nhưng giá trị có thể rỗng. Hai chuyện này dẫn tới hai cách xử lý khác nhau trong mã và không thay thế cho nhau được. Công tắc thuộc tính tùy chọn trong công cụ áp dấu chấm hỏi cho toàn bộ thuộc tính một cách đồng loạt, tiện khi bạn muốn nhanh nhưng không đúng về mặt mô hình, vì trường mã định danh của một tài nguyên gần như chắc chắn luôn có mặt. Cách làm đúng là bật công tắc để có khung, rồi bỏ dấu chấm hỏi ở những trường bạn biết chắc luôn có. Với trường có thể rỗng, hãy sửa từ chữ null đơn độc thành hợp kiểu của kiểu thật với null, và nhớ bật chế độ kiểm tra giá trị rỗng nghiêm ngặt trong cấu hình dự án, vì không bật thì khai báo đó cũng không có tác dụng bảo vệ gì.
Tên khai báo con được đặt từ tên khóa, và điều đó gây ra hai vấn đề
Vấn đề thứ nhất là xung đột tên. Nếu trong dữ liệu của bạn có hai khóa cùng tên ở hai nhánh khác nhau, chẳng hạn cả người mua lẫn người bán đều có khóa address với cấu trúc khác nhau, cả hai đều sinh ra khai báo cùng tên và khai báo tạo sau ghi đè khai báo tạo trước, khiến một trong hai nhánh bị gán sai kiểu. Hãy đếm số khai báo trong kết quả và đối chiếu với số đối tượng lồng trong dữ liệu để phát hiện. Vấn đề thứ hai là quy ước đặt tên: công cụ chỉ viết hoa chữ cái đầu, nên khóa viết theo kiểu gạch dưới sẽ cho ra một tên nửa vời, còn khóa số nhiều dùng cho một mảng sẽ sinh ra khai báo mang tên số nhiều dù nó mô tả một phần tử. Ngoài ra, tên khóa trong JSON không phải lúc nào cũng là định danh hợp lệ của TypeScript: khóa có dấu cách, dấu gạch nối hay bắt đầu bằng chữ số sẽ khiến mã sinh ra sai cú pháp, và bạn phải bọc chúng trong dấu nháy khi khai báo.
Có kiểu rồi thì vẫn cần kiểm tra dữ liệu lúc chạy
Đây là điều dễ quên nhất. Kiểu trong TypeScript chỉ tồn tại ở thời điểm biên dịch và biến mất hoàn toàn trong mã chạy thật; khai báo rằng phản hồi có kiểu này không hề bắt máy chủ trả về đúng như vậy. Nếu bạn ép kiểu cho kết quả trả về của một lời gọi mạng, bạn chỉ đang nói với trình biên dịch hãy tin tôi, và khi máy chủ đổi tên trường hay trả về giá trị rỗng ngoài dự kiến thì lỗi vẫn xảy ra, chỉ khác là nó xảy ra ở chỗ xa hơn và khó lần ra hơn. Cách làm chắc chắn là mô tả cấu trúc bằng một lược đồ kiểm định chạy được lúc thực thi, kiểm tra dữ liệu ngay tại ranh giới với bên ngoài, rồi để thư viện tự suy ra kiểu tĩnh từ chính lược đồ đó; như vậy chỉ có một nguồn định nghĩa duy nhất. Với dự án lớn, hướng bền vững hơn nữa là sinh cả kiểu lẫn hàm gọi từ đặc tả chính thức của giao diện lập trình, để mỗi lần đặc tả đổi thì mã kiểu cũng đổi theo và trình biên dịch chỉ ra ngay chỗ vỡ.
Câu hỏi thường gặp (FAQ)
Nên chọn interface hay type?
Với việc mô tả hình dạng một đối tượng thì hai cách gần như tương đương. Interface có thể được khai báo bổ sung ở nhiều nơi và gộp lại, điều này có lợi khi mở rộng kiểu của thư viện bên ngoài. Type linh hoạt hơn khi cần hợp kiểu, giao kiểu hay kiểu có điều kiện. Quan trọng nhất là theo đúng quy ước sẵn có của dự án.
Vì sao trường có giá trị rỗng lại ra kiểu null?
Vì công cụ chỉ nhìn thấy giá trị rỗng trong mẫu của bạn và không biết kiểu thật của trường đó khi có dữ liệu. Bạn cần sửa tay thành hợp kiểu của kiểu thật với null, và nhớ bật chế độ kiểm tra giá trị rỗng nghiêm ngặt trong cấu hình dự án, vì không bật thì khai báo này cũng không bảo vệ được gì.
Dấu chấm hỏi và hợp kiểu với null khác nhau chỗ nào?
Dấu chấm hỏi nghĩa là thuộc tính có thể không tồn tại trong đối tượng. Hợp kiểu với null nghĩa là thuộc tính luôn tồn tại nhưng giá trị có thể rỗng. Hai tình huống này cần cách xử lý khác nhau trong mã, nên đừng dùng lẫn. Công tắc trong công cụ chỉ thêm dấu chấm hỏi hàng loạt, bạn nên bỏ bớt ở những trường chắc chắn luôn có.
Dán một mảng vào thì kết quả có phải kiểu mảng không?
Không. Khi tầng gốc là mảng đối tượng, công cụ chỉ đọc phần tử đầu tiên và sinh khai báo cho một phần tử. Bạn phải tự viết thêm kiểu mảng của khai báo đó khi dùng. Ngoài ra nếu các phần tử trong mảng khác nhau về cấu trúc thì phần khác biệt không được phản ánh vào kết quả.
Hai khai báo con bị trùng tên thì sao?
Khai báo tạo sau ghi đè khai báo tạo trước, nên một trong hai nhánh sẽ bị gán sai kiểu. Chuyện này xảy ra khi hai khóa cùng tên ở hai nhánh khác nhau có cấu trúc khác nhau. Hãy đếm số khai báo trong kết quả và đối chiếu với số đối tượng lồng trong dữ liệu, rồi đổi tên và tách lại bằng tay.
Trường ngày tháng có được nhận diện không?
Không. Chuỗi ngày tháng theo chuẩn quốc tế vẫn là chuỗi trong JSON nên kiểu sinh ra là string, và điều đó thực ra chính xác về mặt dữ liệu. Nếu bạn muốn làm việc với đối tượng ngày tháng, hãy chuyển đổi tại ranh giới nhận dữ liệu và định nghĩa một kiểu riêng cho phần đã chuyển, đừng khai báo thẳng kiểu ngày cho phản hồi thô.
Vì sao mảng rỗng lại ra kiểu chưa biết?
Vì không có phần tử nào để suy kiểu, và đoán bừa sẽ nguy hiểm hơn là thừa nhận không biết. Bạn nên lấy một mẫu phản hồi khác có dữ liệu trong mảng đó rồi chuyển đổi lại, hoặc điền tay kiểu phần tử dựa trên tài liệu của giao diện lập trình.
Tên khóa có dấu gạch nối hoặc dấu cách thì sao?
Mã sinh ra sẽ sai cú pháp vì những tên đó không phải định danh hợp lệ trong TypeScript. Bạn phải bọc chúng trong dấu nháy khi khai báo, và trong mã thì truy cập bằng cú pháp ngoặc vuông. Nếu có thể, hãy đổi tên trường ngay ở lớp nhận dữ liệu để phần còn lại của mã làm việc với tên sạch.
Kiểu sinh ra có bảo đảm phản hồi thật đúng như vậy không?
Không. Kiểu chỉ tồn tại lúc biên dịch và biến mất trong mã chạy thật, nên khai báo kiểu cho phản hồi chỉ là lời hứa với trình biên dịch. Muốn chắc chắn, hãy kiểm tra dữ liệu bằng một lược đồ kiểm định chạy lúc thực thi ngay tại ranh giới với bên ngoài, rồi suy kiểu tĩnh ra từ chính lược đồ đó.
Nên dán mẫu từ đâu?
Từ tab mạng trong công cụ dành cho lập trình viên, chép nguyên phần thân phản hồi thật của lời gọi bạn quan tâm. Đừng dán ví dụ chép từ tài liệu, vì tài liệu thường lược bớt trường và đôi khi lệch với thực tế. Nếu được, hãy lấy vài mẫu ở các trạng thái khác nhau rồi hợp nhất kiểu bằng tay.
Có sinh được kiểu cho phản hồi nhiều tầng lồng nhau không?
Có, mỗi đối tượng lồng đều được tách thành một khai báo riêng và trường cha nhận tên khai báo đó làm kiểu. Kết quả xếp theo thứ tự khai báo con trước, khai báo gốc cuối cùng. Với dữ liệu lồng sâu, hãy đọc từ dưới lên để nắm cấu trúc tổng thể trước rồi mới xem chi tiết từng tầng.
Dữ liệu tôi dán vào có bị gửi đi đâu không?
Không. Việc phân tích JSON và sinh khai báo đều chạy bằng JavaScript ngay trong trình duyệt, không có bước gọi máy chủ. Nhờ vậy bạn dán được phản hồi thật của giao diện lập trình nội bộ, kể cả khi trong đó có dữ liệu khách hàng, mà không lo lộ ra ngoài.
Từ khóa liên quan
- json to typescript
- sinh interface từ json
- chuyển json sang typescript
- typescript interface là gì
- interface và type khác nhau
- kiểu dữ liệu cho response api
- strictNullChecks typescript
- thuộc tính tùy chọn typescript
- union type trong typescript
- kiểm tra dữ liệu lúc chạy
- zod schema validation
- sinh kiểu từ openapi
- khai báo kiểu cho fetch
- typescript array type
- đặt tên interface chuẩn
- json parse typescript
- type safety khi gọi api
- công cụ typescript online
- generate types from json
- công cụ cho lập trình viên miễn phí
