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. 為什麼要在型別層面做大小寫轉換?
- 防止大小寫不一致:API 回傳的 key 可能是大寫或小寫,使用
Uppercase<T>可以在編譯期保證所有使用者都以同一種大小寫存取。 - 提升 IDE 補全:型別已經固定為大寫/小寫,IDE 能直接給出正確的字串建議。
- 支援模板字串型別:與模板字串結合,可產生更動態的型別(如
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_id、user_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 | 只能處理字串,若鍵名是 symbol 或 number,需額外處理。 |
使用映射型別的條件分支 (K extends string ? Uppercase<K> : K)。 |
最佳實踐
- 在型別層面統一大小寫:盡量把大小寫規則寫在型別上,而不是在程式碼裡跑
toUpperCase()/toLowerCase()。 - 結合映射型別:使用
as Uppercase<K>或as Lowercase<K>讓鍵名自動轉換,省去手動重複寫。 - 抽離別名:若同一個轉換會重複使用,建立別名型別(例如
type UpperKey<T> = Uppercase<keyof T>)提升可讀性。 - 搭配
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_HOST、API_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 會回傳混合大小寫的欄位(userId、UserName),前端想統一使用小寫。利用 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 的道路上越走越遠! 🚀