JSON Sang TypeScript
Dán JSON và lấy ngay lập tức các interface TypeScript.
Cách sử dụng
- Dán JSON của bạn vào bảng bên trái (đối tượng hoặc mảng).
- Nhấp Chuyển đổi · các interface TypeScript xuất hiện bên phải.
- Điều chỉnh tên gốc và kích hoạt các tùy chọn nếu cần.
- Nhấp Sao chép đầu ra để sao chép mã được tạo.
Câu hỏi thường gặp
Nó có xử lý các đối tượng được lồng nhau không?
Có. Mỗi đối tượng được lồng nhau nhận interface được đặt tên riêng. Các phần tử của các mảng cũng được phân tích để xác định kiểu phần tử.
Điều gì xảy ra với các mảng có các kiểu hỗn hợp?
Nếu một mảng chứa các phần tử các kiểu khác nhau, bộ chuyển đổi sử dụng một kiểu union (vd: string | number).
Nó có hỗ trợ các mảng JSON ở gốc không?
Có. Nếu giá trị gốc là một mảng, bộ chuyển đổi phân tích các phần tử của nó và tạo ra interface phù hợp cũng như một bí danh kiểu cho mảng.
Converter Thực Sự Làm Gì
Nó phân tích JSON của bạn, duyệt qua mọi giá trị, và tạo ra một tập hợp các khai báo interface TypeScript mô tả cấu trúc. Các đối tượng lồng nhau đều có interface riêng được đặt tên, dẫn xuất từ khóa thuộc tính cha theo PascalCase (trường user trở thành interface User). Khi hai đối tượng lồng nhau trùng tên, cái thứ hai được thêm hậu tố số (User, User2). Ở gốc, một mảng trở thành bí danh type Root = RootItem[]; cộng với interface RootItem; một đối tượng trở thành một interface Root duy nhất (hoặc bất kỳ tên nào bạn đặt trong trường «tên gốc»).
Đầu vào và đầu ra mẫu:
// Input
{
"id": 42,
"name": "Ada",
"active": true,
"tags": ["admin", "billing"],
"profile": { "bio": "Programmer", "link": null }
}
// Output
export interface Root {
id: number;
name: string;
active: boolean;
tags: string[];
profile: Profile;
}
export interface Profile {
bio: string;
link: unknown;
}
Bảng ánh xạ kiểu JSON → TypeScript
| Giá trị JSON | TS được tạo |
|---|---|
"hello" | string |
42 hoặc 3.14 | number (TS không có kiểu số nguyên riêng) |
true / false | boolean |
null | unknown (converter không thể biết liệu trường có nullable hay chỉ vắng mặt trong mẫu này) |
[] | unknown[] |
[1, 2, 3] | number[] |
[1, "x"] | (number | string)[] |
{ "a": 1 } | interface được đặt tên |
Đối với các mảng đối tượng ở cấp gốc, converter hợp nhất các khóa từ tất cả phần tử thành một interface đại diện duy nhất. Đây là xấp xỉ hợp lý khi các phần tử có cùng cấu trúc, nhưng mất thông tin khi các cấu trúc thực sự phân kỳ (ví dụ: mảng events không đồng nhất). Trong trường hợp đó, bạn cần viết discriminated union bằng tay sau khi tạo.
Tại sao dùng interface cho đối tượng, type cho mảng gốc
TypeScript Handbook chính thức đưa ra gợi ý heuristic một dòng: «Nếu bạn muốn một gợi ý heuristic, hãy dùng interface cho đến khi bạn cần dùng tính năng từ type.» Cả hai đều được định kiểu theo cấu trúc, cả hai đều có thể mô tả cấu trúc đối tượng, nhưng interface vượt trội cho cấu trúc đối tượng vì nó hỗ trợ declaration merging (mở lại interface để thêm trường), hoạt động gọn gàng với extends, và hiển thị theo tên trong lỗi trình biên dịch. Các bí danh type là bắt buộc khi bạn cần union của các kiểu nguyên thủy, intersection của nhiều kiểu, hoặc đặt tên cho thứ không phải đối tượng, đó là lý do mảng ở cấp gốc nhận bí danh type Root = RootItem[]; thay vì interface.
Ghi chú về đặt tên: converter dùng PascalCase không có tiền tố I cổ điển (dùng User thay vì IUser). Các style guide TS hiện đại (của Google, Microsoft, cộng đồng React) hầu như đều bỏ tiền tố này. Tên thuộc tính khớp chính xác với khóa JSON (camelCase nếu JSON dùng camelCase, snake_case nếu dùng snake_case). Các khóa không phải định danh JS hợp lệ (chứa dấu gạch ngang, dấu cách, hoặc bắt đầu bằng chữ số) được đặt trong dấu ngoặc kép: "foo-bar": string;.
Những Gì Công Cụ Này Không Làm (Trung Thực về Giới Hạn)
Một ví dụ JSON đơn lẻ mang ít thông tin hơn schema thực, vì vậy một số điều muốn phát hiện là không thể chỉ từ đầu vào:
- Trường tùy chọn. Mọi thuộc tính trong ví dụ của bạn đều trở thành bắt buộc. Nếu API thực đôi khi bỏ qua một trường, bạn cần thêm
?bằng tay:email?: string;. Hoặc chạy converter trên nhiều ví dụ và union kết quả. - Phát hiện nullable. Một
nulltrong ví dụ trở thànhunknown; converter không thể biết liệu trường có thực sự nullable (string | null) hay chỉ tình cờ null trong mẫu này. Hãy điều chỉnh bằng tay dựa trên hợp đồng API. - Union kiểu literal string. Trường status với giá trị
"active"trở thànhstring, không phải"active" | "inactive" | "pending". Converter không thể thấy các giá trị khác. - Phát hiện kiểu Date. Chuỗi ISO 8601 như
"2024-04-12T10:00:00Z"vẫn làstring.Datecủa JavaScript không phải là kiểu JSON. - Discriminated union. Nếu mảng gốc chứa các phần tử có cấu trúc khác nhau (sự kiện của các loại khác nhau, bản ghi đa hình), converter hợp nhất tất cả khóa thành một interface thay vì tạo tagged union. Với dữ liệu thực sự không đồng nhất, bạn cần viết union bằng tay.
- Độ chính xác số.
numbercủa JavaScript biểu diễn an toàn các số nguyên lên đến 253−1. Các ID số nguyên lớn hơn bị mất độ chính xác khi được phân tích bởiJSON.parse; nếu API của bạn trả về số nguyên 64-bit, mẫu an toàn nhất là gửi chúng dưới dạng chuỗi và đặt kiểu làstring.
Khi Nào Nên Dùng Công Cụ Này
- Khởi tạo kiểu từ phản hồi API thực. Gọi endpoint, dán JSON, nhận tập hợp interface ban đầu. Chỉnh sửa bằng tay cho các trường tùy chọn và string literal.
- Thêm kiểu vào SDK bên thứ ba chưa có kiểu. Nhiều thư viện JS cũ không có kiểu. Tạo từ phản hồi mẫu nhanh hơn đọc tài liệu.
- Chia sẻ kiểu giữa front-end và back-end. Tạo từ một ví dụ chuẩn và sao chép vào cả hai codebase (hoặc chia sẻ qua package kiểu được xuất bản).
- Di chuyển codebase JS sang TypeScript. Tạo kiểu từ dữ liệu thực chạy qua ứng dụng và dần dần chú thích chữ ký hàm.
- Tạo kiểu cho file cấu hình. Kiểu chặt chẽ giúp lỗi cấu hình hiển thị lúc biên dịch.
- Ghi lại cấu trúc của file dữ liệu nội bộ. Interface được tạo ra đồng thời là tài liệu có thể đọc bằng máy.
Code-First vs Schema-First vs Trực tiếp (Công cụ này thuộc nhóm nào)
Ba quy trình phổ biến để lấy kiểu TypeScript từ dữ liệu runtime:
- Code-first runtime validators: Zod, Yup, Effect Schema, Joi. Bạn viết validator schema trong JS, nó cung cấp cả runtime parsing lẫn kiểu TypeScript qua suy luận. Schema là nguồn sự thật. Tốt nhất khi validation quan trọng không kém kiểu.
- Schema-first: viết JSON Schema hoặc OpenAPI spec, tạo kiểu TypeScript qua các công cụ như
quicktype,json-schema-to-typescript, hoặcopenapi-typescript. Tốt nhất khi hợp đồng API trải rộng nhiều ngôn ngữ. - Suy luận trực tiếp từ mẫu (công cụ này, cộng với chế độ JSON thuần của quicktype), dán dữ liệu thực, nhận kiểu. Con đường nhanh nhất; đảm bảo validation yếu nhất vì không có gì kiểm tra dữ liệu runtime so với kiểu.
Lựa chọn đúng phụ thuộc vào mối quan tâm của bạn là chủ yếu kiểm tra kiểu tĩnh (công cụ này rất tốt) hay còn cả an toàn runtime (hãy dùng Zod / Effect Schema thay thế).
Những Lỗi Phổ Biến
- Coi kiểu được tạo là spec hoàn chỉnh. Chúng là điểm khởi đầu hoàn thành 70%. Thêm
?cho các trường tùy chọn,| nullnơi thực sự nullable, string literal union nơi có thể. - Tạo từ ví dụ đơn lẻ khi API có biến thể. Đối tượng user đôi khi có
emailvà đôi khi không sẽ tạo ra «email là bắt buộc». Chạy nhiều ví dụ và union kết quả, hoặc chỉnh sửa bằng tay. - Đánh dấu tất cả
readonlytự động. Hữu ích cho các luồng dữ liệu bất biến nhưng sai cho các đối tượng trạng thái bạn thay đổi. Hãy dùng togglereadonlycó chủ đích. - Tin tưởng độ chính xác trên ID số nguyên lớn.
numbercủa JavaScript tối đa là 253−1. Với ID 64-bit, hãy truyền dưới dạng chuỗi và đặt kiểu làstring. - Nhầm lẫn
interfacevớitype. Các công cụ khác nhau, sự đánh đổi khác nhau. Gợi ý heuristic của TypeScript Handbook là «hãy dùng interface trừ khi bạn cần tính năng chỉ type mới có,» chính xác là những gì công cụ này làm. - Dán JSON bảo mật vào converter phía máy chủ. Phản hồi API thực thường chứa dữ liệu khách hàng, thông tin xác thực, ID nội bộ. Chuyển đổi chỉ trên trình duyệt (công cụ này) giữ dữ liệu trên máy của bạn.
Câu Hỏi Thường Gặp Thêm
Tại sao tất cả trường của tôi được đánh dấu là bắt buộc?
Vì một ví dụ JSON đơn lẻ không thể cho converter biết trường nào đôi khi vắng mặt trong API thực. Mọi khóa xuất hiện trong ví dụ đều trở thành bắt buộc. Nếu bạn biết một trường là tùy chọn, hãy thêm ? ở cuối bằng tay: email?: string;. Để phát hiện tùy chọn đa mẫu, CLI quicktype phức tạp hơn xử lý điều này một cách tự nhiên.
Tại sao trường null của tôi có kiểu là unknown?
Vì converter không thể biết từ một null đơn lẻ liệu trường có luôn nullable (string | null) hay chỉ tình cờ null trong mẫu này (string). unknown là mặc định an toàn; TypeScript sẽ buộc bạn thu hẹp kiểu trước khi dùng giá trị, an toàn hơn any. Hãy chỉnh sửa thành string | null bằng tay khi bạn biết ý định của API.
Tôi nên dùng interface hay type?
Gợi ý heuristic chính thức của TypeScript Handbook: dùng interface cho đến khi bạn cần tính năng chỉ type mới có, chủ yếu là union type, intersection type, hoặc đặt tên cho một kiểu nguyên thủy. Converter này tuân theo quy tắc đó: interface cho mọi cấu trúc đối tượng, type cho bí danh mảng gốc. Một số style guide khuyến nghị mặc định dùng type thay thế (Matt Pocock đã lập luận điều này công khai) nhưng mặc định thông thường là interface cho đối tượng.
JSON của tôi có được tải lên đâu không?
Không. Quá trình chuyển đổi là một JavaScript IIFE độc lập phân tích JSON của bạn qua JSON.parse, duyệt qua giá trị, và ghi kết quả TypeScript vào textarea. Không có fetch, không có lời gọi analytics mang nội dung JSON, không có máy chủ. Điều này quan trọng vì phản hồi API thực thường chứa dữ liệu khách hàng, ID nội bộ, hoặc thông tin xác thực mà bạn không muốn đi qua bên thứ ba.
JSDoc, Zod hoặc runtime validation thì sao?
Công cụ này chỉ tạo kiểu TypeScript lúc biên dịch: không có runtime validator (không có Zod / Yup / Effect Schema), không có comment JSDoc. Nếu bạn cần runtime validation đồng thời là kiểu tĩnh, hãy viết schema trong Zod và dùng z.infer<typeof Schema> của Zod để suy ra kiểu. Nếu bạn có JSON Schema và muốn cả runtime validation lẫn kiểu TS, json-schema-to-typescript + AJV là sự kết hợp thông thường.
Tại sao tên interface không có tiền tố I?
Vì hầu hết các style guide TypeScript hiện đại đều bỏ nó. TypeScript Style Guide của Google khuyến nghị chống lại nó rõ ràng; cộng đồng React đã thống nhất dùng PascalCase không có tiền tố. Quy ước IUser cổ điển đến từ ảnh hưởng của C# / Java đối với TypeScript ban đầu; thực hành hiện tại là dùng User thuần túy.