本文 AI 產出,尚未審核

TypeScript 工具型別:Uppercase<T>Lowercase<T>


簡介

在大型前端或 Node.js 專案中,字串常常被用來作為 enum、路由、API key、CSS class 等識別子。若這些字串在不同層級或不同檔案間需要統一大小寫,手動維護會很容易出錯,且在重構時也會產生大量的搜尋/取代工作。

TypeScript 內建的 工具型別 Uppercase<T>Lowercase<T> 可以在 編譯期 把字串型別自動轉換為全大寫或全小寫,讓我們在型別層面就保證字串的大小寫一致性,減少 runtime 錯誤與維護成本。

本文將從概念說明、實作範例、常見陷阱與最佳實踐,直到真實專案中的應用場景,完整介紹這兩個工具型別的使用方法,幫助 初學者到中階開發者 在日常開發中更安全、更有效率地操作字串型別。


核心概念

1. Uppercase<T>Lowercase<T> 的語法

type Uppercase<T extends string> = /* 內建實作,將 T 內的每個字元轉為大寫 */
type Lowercase<T extends string> = /* 內建實作,將 T 內的每個字元轉為小寫 */
  • T 必須是 字串型別(或字串字面值型別),否則會產生編譯錯誤。
  • 這兩個型別 不會改變值,只會在型別層面產生新的字串型別。例如:
type Foo = Uppercase<"helloWorld">;   // -> "HELLOWORLD"
type Bar = Lowercase<"TypeScript">;  // -> "typescript"

2. 為什麼要在型別層面做大小寫轉換?

  1. 防止大小寫不一致:API 回傳的 key 可能是大寫或小寫,使用 Uppercase<T> 可以在編譯期保證所有使用者都以同一種大小寫存取。
  2. 提升 IDE 補全:型別已經固定為大寫/小寫,IDE 能直接給出正確的字串建議。
  3. 支援模板字串型別:與模板字串結合,可產生更動態的型別(如 Uppercase<\${K}_id`>`)。

3. 與其他工具型別的關係

工具型別 目的 範例
Uppercase<T> 轉為全大寫 Uppercase<"abc">"ABC"
Lowercase<T> 轉為全小寫 Lowercase<"ABC">"abc"
Capitalize<T> 首字母大寫 Capitalize<"hello">"Hello"
Uncapitalize<T> 首字母小寫 Uncapitalize<"Hello">"hello"

這些工具型別可以互相組合,形成如 Uppercase<Capitalize<"foo">> 的複雜型別。


程式碼範例

以下示範 5 個實用案例,說明如何在不同情境中運用 Uppercase<T>Lowercase<T>

範例 1:統一 API 回傳的欄位名稱

假設後端回傳的 JSON 欄位是大寫,但前端想以小寫存取:

type ApiResponse = {
  USER_ID: string;
  USER_NAME: string;
};

// 使用 Lowercase 產生對應的前端型別
type FrontendUser = {
  [K in keyof ApiResponse as Lowercase<K>]: ApiResponse[K];
};

/* 結果:
type FrontendUser = {
  user_id: string;
  user_name: string;
}
*/

重點as Lowercase<K> 讓映射型別的鍵名自動轉為小寫,避免手動寫 user_iduser_name

範例 2:字串常數的大小寫轉換

在 Redux action type 中,常會使用全大寫字串作為常數:

type Action = "addItem" | "removeItem" | "clearAll";

// 產生對應的常數型別
type ActionConst = Uppercase<Action>;

/* 結果:
type ActionConst = "ADDITEM" | "REMOVEITEM" | "CLEARALL"
*/

這樣可以在 switch 語句裡直接使用大寫常數,保持風格一致。

範例 3:結合模板字串與 Uppercase

type Entity = "user" | "product";

type TableName = Uppercase<`${Entity}_table`>;
// -> "USER_TABLE" | "PRODUCT_TABLE"

透過模板字串,我們只要定義一次 Entity,就能自動產生所有資料表名稱的大寫字串。

範例 4:建立「大小寫不敏感」的映射型別

有時候需要根據使用者輸入的 key(不分大小寫)取得對應值:

type ConfigMap = {
  "HOST": string;
  "PORT": number;
};

// 產生一個允許任意大小寫的映射
type InsensitiveConfigMap = {
  [K in keyof ConfigMap as Uppercase<K> | Lowercase<K>]: ConfigMap[K];
};

/* 結果:
type InsensitiveConfigMap = {
  HOST: string;   // 大寫
  host: string;   // 小寫
  PORT: number;
  port: number;
}
*/

技巧:使用聯集 (Uppercase<K> | Lowercase<K>) 同時提供兩種鍵名,讓使用者可以自由輸入。

範例 5:限制函式參數只能是特定大小寫的字串

function setLocale<T extends string>(locale: Uppercase<T>) {
  // locale 必須是大寫字串,例如 "EN-US"
  console.log(`Locale set to ${locale}`);
}

// 正確使用
setLocale("en-us");   // ✅ 編譯器會自動把 "en-us" 轉為 Uppercase<"en-us"> => "EN-US"

// 錯誤示範
setLocale("zh_TW");  // ❌ 不是全大寫,編譯錯誤

Uppercase<T> 把參數型別限制為全大寫,避免因大小寫不一致而產生的錯誤。


常見陷阱與最佳實踐

陷阱 說明 解決方式
傳入非字串型別 Uppercase<number> 會直接報錯。 確保 T extends string,或先使用 Extract<T, string> 取得字串子集合。
字串字面值過長 編譯器在處理非常長的字串(> 2⁶⁴)時可能效能下降。 盡量只在 字面值型別較短的模板字串 上使用。
any 混用失效 Uppercase<any> 會退化為 any,失去型別檢查。 避免在公開 API 中接受 any,改用 unknown + 型別保護。
過度使用導致型別膨脹 多層嵌套的 Uppercase<...> 可能使型別變得難以讀懂。 只在需要的地方使用,必要時使用 type Alias = Uppercase<...> 抽離。
不支援 Symbol、Number 只能處理字串,若鍵名是 symbolnumber,需額外處理。 使用映射型別的條件分支 (K extends string ? Uppercase<K> : K)。

最佳實踐

  1. 在型別層面統一大小寫:盡量把大小寫規則寫在型別上,而不是在程式碼裡跑 toUpperCase() / toLowerCase()
  2. 結合映射型別:使用 as Uppercase<K>as Lowercase<K> 讓鍵名自動轉換,省去手動重複寫。
  3. 抽離別名:若同一個轉換會重複使用,建立別名型別(例如 type UpperKey<T> = Uppercase<keyof T>)提升可讀性。
  4. 搭配 as const:在常量陣列或物件上使用 as const,讓 TypeScript 推斷出字面值型別,才能正確套用 Uppercase<T>

實際應用場景

1. 多語系(i18n)鍵值的統一

在大型多語系專案中,翻譯檔的鍵名常以全大寫或全小寫呈現。透過 Uppercase<T> 可以在 type 定義 中一次性把所有鍵名轉為大寫,確保程式碼與翻譯檔一致:

type I18nKeys = "home.title" | "home.welcome" | "profile.edit";

// 產生全大寫的鍵名,用於 UI 組件
type I18nConst = Uppercase<I18nKeys>;
/* -> "HOME.TITLE" | "HOME.WELCOME" | "PROFILE.EDIT" */

2. 環境變數的自動映射

Node.js 常透過 process.env 讀取環境變數,變數名稱通常是全大寫(DB_HOSTAPI_KEY)。若我們想把它們映射到一個 設定物件,可以使用 Uppercase<keyof T>

type EnvKeys = "dbHost" | "apiKey";

type EnvMap = {
  [K in EnvKeys as Uppercase<K>]: string;
};

/* 結果:
type EnvMap = {
  DBHOST: string;
  APIKEY: string;
}
*/

這樣在讀取環境變數時,就不會因大小寫寫錯而找不到值。

3. GraphQL / REST API 的欄位正規化

某些後端 API 會回傳混合大小寫的欄位(userIdUserName),前端想統一使用小寫。利用 Lowercase<T> 搭配映射型別,可一次完成正規化:

type RawUser = {
  UserId: number;
  UserName: string;
  EmailAddress: string;
};

type NormalizedUser = {
  [K in keyof RawUser as Lowercase<K>]: RawUser[K];
};

/* -> {
  userid: number;
  username: string;
  emailaddress: string;
} */

總結

  • Uppercase<T>Lowercase<T>編譯期字串大小寫轉換 的工具型別,只接受字串型別作為參數。
  • 它們能夠在 映射型別、模板字串型別 中靈活運用,讓鍵名或字面值一次性完成大小寫統一。
  • 常見的陷阱包括非字串型別、過度嵌套導致型別膨脹、以及與 any 混用失效。透過 條件型別、別名抽離 等技巧,可有效避免這些問題。
  • 在實務上,這兩個型別特別適合 API 回傳正規化、環境變數映射、i18n 鍵值統一、Redux action 常數 等情境,能減少 runtime 錯誤、提升 IDE 補全與程式碼可讀性。

掌握 Uppercase<T> / Lowercase<T> 後,你就能在 型別層面 為字串大小寫把關,讓 TypeScript 的型別系統發揮出更大的威力,寫出更安全、更維護友好的程式碼。祝你在 TypeScript 的道路上越走越遠! 🚀