JSON 轉 TypeScript
將 JSON 資料自動轉換為 TypeScript 接口或類型定義
// 輸入 JSON 後自動生成 TypeScript 類型定義什麼是 JSON 轉 TypeScript?
JSON 轉 TypeScript 工具會分析 JSON 資料結構,並產生對應的 TypeScript interface 或 type 定義。它適合把 API 回應、設定檔、範例 payload、Mock 資料快速接入前端或 Node.js 專案,讓編輯器補全、型別檢查和重構更可靠。工具可以辨識物件、陣列、巢狀欄位、可選屬性和基本值型別,把原始資料轉成型別定義的起點。但產生結果仍需要人工判斷:一份範例 JSON 未必涵蓋所有欄位,空陣列無法說明元素結構,真實 API 也可能回傳 null 或多種物件形態。
使用方式
使用方式
- 在左側輸入框中貼上 JSON 資料,或點選範例按鈕載入示範資料
- 設定選項:指定根型別名稱、選擇 interface 或 type 樣式、是否加上 export 等
- 對應的 TypeScript 型別定義會自動產生在右側
- 點選「複製」按鈕將產生的型別定義複製到剪貼簿
- 將型別定義貼入 TypeScript 專案中即可使用
型別產生提示
- 請使用具代表性的 JSON 範例。單一範例無法涵蓋實際 API 中所有選填欄位、可為 null 的值或聯集型別。
- 提交前請檢查自動產生的名稱,特別是巢狀物件的 interface 名稱可能過於通用。
使用場景
技術原理
JSON 轉 TypeScript 本質上是一個「結構推斷」的過程。工具會遞迴走訪 JSON 樹的每個節點,將每個值的執行期型別映射為 TypeScript 型別:string → string、number → number、boolean → boolean、null → null、object → 子介面、array → 元素型別的陣列。輸出結果是一組 TypeScript `interface` 宣告(若選擇了 type 輸出模式則為 `type` 別名)加上從物件值欄位中提取的巢狀介面。 推斷中最困難的部分是陣列的聯集型別判定。當陣列中每個元素的型別相同時,輸出為 `T[]`(同質陣列);當元素型別不同時,輸出為這些型別的聯集:`[1, 'a', true]` 會變成 `(number | string | boolean)[]`。當輸入是一個混合了字面值和結構化值的物件(例如 `{ id: 1, name: 'Alice', tags: ['admin', 'editor'] }`),每個欄位會獲得各自的型別;對於混合基本值的物件,頁面會發出單一介面而非聯集,因為這才是 TypeScript 程式碼所預期的(也符合 JSON 物件作為記錄型別的本質)。 多個範例物件(典型使用情境:貼上 JSON 陣列)會通過交集它們的形狀來合併。在每個範例中都出現的欄位成為必填;在任一範例中缺失的欄位成為可選(`fieldName?: T`)。這是 API 回應的正確行為:200 OK 回應是一種形狀,404 回應是另一種形狀,兩者的聯集就是正確的 TS 型別。對於單一範例,每個欄位都是必填的。 名稱正規化是大多數「JSON 轉 TS」工具的短處。JSON 鍵通常使用 snake_case(`user_id`、`created_at`)或 camelCase(`userId`、`createdAt`);TypeScript 的慣例是介面名稱使用 PascalCase(`UserProfile`、`AuditEntry`),屬性名稱使用 camelCase。頁面會將 JSON 鍵轉換為 camelCase(若已是 camelCase 或單字則保留),介面名稱轉換為 PascalCase。巢狀物件會被提取為具名介面:`{ user: { name, age } }` 會變成 `interface User { name: string; age: number; }`,父層則變成 `interface Root { user: User; }`。遞迴結構(具有 `children: TreeNode[]` 欄位的樹節點)會使用前置 `type` 宣告而非 `interface` 來建立自引用,使循環在 TypeScript 型別檢查器中定義明確。 「根型別」切換決定了陣列根與物件根的差異。JSON 陣列會變成 `type Root = Item[]` 或 `interface Root { items: Item[]; ... }`(取決於 wrap-array 開關)。JSON 物件則在頂層產生一個扁平介面。日期字串(RFC 3339 / ISO 8601)可以被標記為 `Date` 而非 `string`;當嚴格模式開關開啟時,UUID、email 和 URL 可以被標記為品牌型別(`type UUID = string & { readonly __brand: 'UUID' };`)。 JSDoc 的產生是啟發式的:名為 `email` 的欄位(且其值符合基本 email 正則表達式)會在宣告上方加上 `/** user email */` 這樣的註解。`id`、`name`、`url`、`created_at`/`createdAt`、`updated_at`/`updatedAt`、`description`、`title` 等欄位也同樣處理。這些註解僅供參考,不代表人工編寫的品質;它們是一個起點,供人類進一步修改。頁面還會發出 `export` 修飾詞,使輸出可以直接放入任何 TS 檔案,並加上 `// generated by json-to-typescript, do not edit by hand` 標頭以防止手動編輯在下次 Schema 重新產生時被覆蓋。
- 原子型別映射:JSON 的 string/number/boolean/null 一對一映射到 TypeScript 的 string/number/boolean/null。大整數(>2^53)需要 BigInt;頁面會在值超過 Number.MAX_SAFE_INTEGER 時將其標記為 `bigint`。
- 陣列型別推斷:走訪所有元素並取其型別聯集;相同型別折疊為 `T[]`,不同型別組成 `(A | B | C)[]`。空陣列會變成 `unknown[]`,因為元素型別未知。
- 巢狀物件提取:巢狀物件會自動提升為具名介面。遞迴結構使用 `type Name = ...`(而非 `interface`),使前置引用在 TypeScript 型別檢查器中定義明確。
- 可選欄位偵測:當多個範例物件合併時,在每個範例中都出現的欄位成為必填;在任一範例中缺失的欄位成為可選(`fieldName?: T`)。單一範例輸入則將每個欄位標記為必填。
- 命名慣例:JSON 鍵轉換為 camelCase 作為屬性名稱(user_id → userId、created_at → createdAt),介面名稱使用 PascalCase(UserProfile、AuditEntry)。單字鍵保留原樣;鍵中的數字保留;前導數字會加上底線前綴以保持合法的 TS 識別碼。
- 根型別切換:陣列根 → `export type Root = Item[]`(若 wrap-array 開啟則包裝在 `{ items: Item[]; }` 中);物件根 → 單一 `export interface Root { ... }`。遞迴結構會先產生一個前置存根宣告。
- JSDoc 註解:對已知欄位名稱(email、id、name、url、createdAt、updatedAt、description、title)進行啟發式註解產生。輸出包含 `export` 修飾詞和 `// generated, do not edit by hand` 標頭以防止手動編輯。
- 嚴格模式:可選啟用品牌型別(UUID、Email、URL、Date)、readonly 修飾詞、exactOptionalPropertyTypes 和 noUncheckedIndexedAccess。當嚴格模式開啟時,頁面會發出 `type UUID = string & { readonly __brand: 'UUID' };`,使下游程式碼可以區分原始字串和型別化值。
範例
簡單物件轉 interface
{"id": 1, "name": "Alice", "active": true}
->
interface User {
id: number;
name: string;
active: boolean;
}巢狀物件產生子介面
{"user": {"name": "Alice", "address": {"city": "NYC"}}}
->
interface Root {
user: User;
}
interface User {
name: string;
address: Address;
}
interface Address {
city: string;
}陣列轉 readonly Tuple
[{"id": 1}, {"id": 2}]
->
interface Item {
id: number;
}
type Root = readonly Item[];常見問題
它會產生哪些 TypeScript 結構?
預設使用 interface——每種物件結構對應一個。選填欄位以 ? 標記。陣列會變成 T[]。混合型別陣列會變成聯集。能識別的字串格式(如 ISO 日期)仍維持為 string,除非您主動啟用 branded types。部分頁面也提供改用 type 別名而非 interface 的選項。
巢狀物件如何處理?
每個不同的巢狀結構都會產生自己的介面,並依屬性路徑命名(User、UserAddress、UserPreferences)。若有兩個屬性結構相同,產生器會視設定決定是否去重——請檢視輸出,必要時手動合併。
屬性是必填還是選填?
預設情況下,範例中出現的屬性都標記為必填。在某些陣列元素中出現、其他元素中沒有的屬性,會以 `?` 標記為選填。如果您的範例只是眾多可能結構的其中一個,可切換為「全部選填」。
如何處理 null 與 undefined?
JSON 中的 null 在 TypeScript 中對應 `null`(不是 undefined)。如果某屬性有時是字串、有時是 null,會變成 `string | null`。JSON 沒有 undefined,所以產生器永遠不會直接輸出 `undefined`。
聯集型別與可區分聯集(discriminated union)呢?
當陣列包含混合的物件結構時,產生器會輸出多個介面的聯集。它不會自動偵測判別欄位;如果您想要完整的型別窄化能力,請手動改寫成可區分聯集(例如使用 'kind' 標籤)。
JSON 會被上傳嗎?
不會。轉換在您的瀏覽器中透過 JS 的 JSON 解析器與字串樣板完成。貼上的 JSON 不會離開您的裝置。
產生的型別會和我後端實際回傳的相符嗎?
只能與您提供的範例一樣準。執行時的資料一定要再透過 schema(Zod、io-ts、Yup)驗證——TypeScript 型別在執行時會被抹除,無法捕捉後端與範例脫節的情況。