TypeScript 物件鍵名指南

在 TypeScript 將 Object Keys 轉為 camelCase

snake_case API payload 轉為 camelCase object 時,常同時需要 runtime transformation 與 compile-time shape。

作者 Camel Case Converter 編輯團隊發布於 2026年9月12日最後審閱

TypeScript 實作

type CamelCase<S extends string> =
  S extends `${infer Head}_${infer Tail}`
    ? `${Lowercase<Head>}${Capitalize<CamelCase<Tail>>}`
    : Uncapitalize<S>

type Camelize<T> =
  T extends readonly (infer Item)[] ? Camelize<Item>[] :
  T extends Record<string, unknown> ? { [K in keyof T as K extends string ? CamelCase<K> : K]: Camelize<T[K]> } : T

function toCamelKey(key: string) {
  return key.replace(/[-_]+([a-zA-Z0-9])/g, (_, char: string) => char.toUpperCase())
}

function keysToCamel<T>(value: T): Camelize<T> {
  if (Array.isArray(value)) return value.map(keysToCamel) as Camelize<T>
  if (value === null || typeof value !== "object") return value as Camelize<T>
  const output: Record<string, unknown> = {}
  for (const [key, child] of Object.entries(value)) {
    const nextKey = toCamelKey(key)
    if (Object.hasOwn(output, nextKey)) throw new Error(`Key collision: ${nextKey}`)
    output[nextKey] = keysToCamel(child)
  }
  return output as Camelize<T>
}

遞迴 mapped type 可描述重新命名的 string key 並保留 array element 結構。

type 不會改變 network data,因此 runtime function 仍然必須存在。

正式環境邊界案例

  • 複雜縮寫規則可能讓 runtime 與 type-level 實作結果不同。
  • 即使 type 看起來有效,key collision 仍是 runtime data 問題。
  • Symbol key 與非 JSON instance 應明確處理。
  • 若 runtime 保留語意前綴,精確型別有需求時 type layer 也應反映例外。

線上測試物件鍵名

貼上 JSON 物件,測試遞迴轉換、只轉最上層、排除鍵名、_/$ 前綴與衝突處理。

拖放 .json 檔案,或從裝置選取

檔案只會在瀏覽器中讀取。最大檔案大小:5 MB。

轉換後 JSON
{
  "userProfile": {
    "firstName": "Ada",
    "createdAt": "2026-09-12"
  }
}

解析、檔案讀取、格式化與鍵名轉換都在瀏覽器本機執行。JSON 不會傳送到轉換伺服器。