首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau
百百 BLOG
首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau

Kurau Blog

「隨心而寫,真真假假,都是我」

一個記錄生活、輸出興趣的個人空間。
遊戲、影視、閱讀、學習……每一段體驗都值得留下文字。

頁面導覽

  • 學習紀錄
  • 遊戲心得
  • 影視Life
  • 書單
  • 委託作品與二創
  • Kurau
  • 合作邀請

找到我

歡迎來 Discord 找我聊天!

“曾經發生的事不可能忘記,只是暫時想不起來而已。”-《神隱少女》

© 2026 Kurau All rights reserved

面試考題

TypeScript 資料型別 - 元組(Tuple) & 列舉(Enum)

By Kurau·Updated 2026-06-02·7 分鐘閱讀

TypeScript 在 array 與 object 之外,提供兩種「結構化常用型別」:元組(Tuple) 把一個陣列的「每個位置」鎖定型別與長度;列舉(Enum) 把一組命名常數收成一個型別。兩者都是面試常考的「你知不知道它的坑」題型。


元組(Tuple)

元組是固定長度 + 每個位置型別固定的陣列。一般 array 是「同一型別、長度任意」,元組是「位置即型別」。

// 一般 array:同型別、長度任意
let nums: number[] = [1, 2, 3];

// 元組:位置 0 必須 string、位置 1 必須 number,長度固定為 2
let user: [string, number] = ['Alice', 25];

user = [25, 'Alice']; // ❌ 型別順序錯
user = ['Alice'];     // ❌ 長度不足
user = ['Alice', 25, true]; // ❌ 超出長度
ts

Labeled Tuple(具名元組)

只是標籤,不影響型別,純粹增加可讀性(IDE 提示會顯示名稱)。

type Coord = [x: number, y: number];
type Range = [start: number, end: number];

const c: Coord = [10, 20];
ts
標籤要嘛全標、要嘛全不標,不能混用:[x: number, number] 會編譯錯誤。

Optional element(可選元素)

用 ? 標記可選元素,只能放在尾端,且會放寬長度限制。

let point: [number, number, number?] = [10, 20];      // z 可省略
point = [10, 20, 30];                                  // ✅

// ❌ 可選元素不能在必填元素前面
type Bad = [a?: number, b: number]; // Error
ts

Rest element(其餘元素)

用 ...T[] 表示「前面固定、後面不定長」,可放在任意位置。

// 第一個是 string,後面任意數量 number
type StringThenNumbers = [string, ...number[]];
const a: StringThenNumbers = ['sum', 1, 2, 3]; // ✅

// rest 也可放開頭或中間
type AtLeastOne = [...string[], number]; // 結尾必為 number
ts

最經典的用途是替函式 arguments 或可變參數定型:

function concat<T extends unknown[]>(...args: [...T, string]): T {
  // ...
  return args.slice(0, -1) as T;
}
ts

唯讀元組(readonly tuple)

加 readonly 後元素不可被重新賦值或 mutate,常配合 as const 使用。

const rgb: readonly [number, number, number] = [255, 0, 0];
rgb[0] = 0;       // ❌ Cannot assign to '0' because it is read-only
rgb.push(1);      // ❌ readonly tuple 沒有 push

// as const 產生的就是 readonly tuple
const point = [10, 20] as const; // type: readonly [10, 20]
ts

常見用途與陷阱

// React useState 回傳值本身就是 tuple
const [count, setCount] = useState(0);
// type: [number, Dispatch<SetStateAction<number>>]
ts
陷阱:push 能繞過長度限制
非 readonly 元組仍是陣列,TS 不會擋 push:
let t: [string, number] = ['a', 1];
t.push(2);        // ⚠️ 不報錯,runtime 變成長度 3
console.log(t[2]); // 但用 index 2 存取會型別報錯
ts

真的要鎖死長度,用 readonly 元組。

寫法長度可變用途
[string, number]固定 2(但 push 可繞過)✅一般成對資料
[string, number?]1 或 2✅尾端可省略
[string, ...number[]]≥ 1✅可變參數定型
readonly [string, number]固定 2(鎖死)❌不可變的座標/常數

列舉(Enum)

enum 用於定義一組命名常數,提升可讀性,並把這組值收斂成一個型別。

數字列舉(Numeric Enum)

不給值時從 0 開始自動遞增;指定某個值後,後續成員從該值繼續遞增。

enum Direction {
  Up,        // 0
  Down,      // 1
  Left,      // 2
  Right,     // 3
}

enum Code {
  A = 5,
  B,         // 6(接續遞增)
  C,         // 7
}
ts

數字列舉支援反向對映(reverse mapping):既能 名稱 → 值,也能 值 → 名稱。

Direction.Up;      // 0
Direction[0];      // 'Up'  ← 反向對映
ts

原理是編譯後產生雙向 key:

// 編譯結果(節錄)
var Direction;
(function (Direction) {
  Direction[Direction["Up"] = 0] = "Up";
  Direction[Direction["Down"] = 1] = "Down";
})(Direction || (Direction = {}));
js

字串列舉(String Enum)

每個成員都必須明確賦值(不會自動遞增)。

enum Status {
  Active = 'ACTIVE',
  Inactive = 'INACTIVE',
  Pending = 'PENDING',
}
ts
字串列舉沒有反向對映:Status['ACTIVE'] 是 undefined。
只有數字列舉才產生 值 → 名稱 的反向 key。

字串列舉通常比數字列舉更實用:debug log / 序列化時看到的是 'ACTIVE' 而不是難懂的 0。

別混用會踩雷
enum Mixed {
  A,            // 0
  B = 'TWO',    // 字串
  C,            // ❌ 前一個是字串,C 無法自動遞增 → 報錯
}
ts

const enum

加 const 後,編譯時會把每個用到的地方直接內聯成字面值(inline),不產生對應的 JS 物件。

const enum HttpStatus {
  OK = 200,
  NotFound = 404,
}
const s = HttpStatus.OK;
// 編譯後 → const s = 200; (HttpStatus 物件整個消失)
ts
一般 enumconst enum
編譯產物產生 runtime 物件內聯字面值,無物件
反向對映數字 enum 支援❌ 不支援(物件不存在)
動態存取 E[key]✅❌(無物件可查)
bundle 體積較大較小
const enum 的隔離編譯問題
在 isolatedModules(Babel / esbuild / SWC 等單檔轉譯器)下,const enum 無法跨檔內聯,會出錯或行為不符。Vite / Next.js SWC 這類工具鏈要特別注意,官方也建議一般情況避免 const enum。

Enum vs Union Type vs as const

現代 TypeScript 中,很多人偏好用 union type 或 as const object 取代 enum。核心原因:enum 會產生額外的 runtime 程式碼(它不是純型別,是真實存在的 JS 物件),而 union / as const 是「型別層的東西」,零 runtime 成本、tree-shaking 友善。

// 1. Enum:有 runtime 物件
enum Color { Red = 'RED', Green = 'GREEN' }

// 2. Union type:純型別,零 runtime
type Color = 'RED' | 'GREEN';

// 3. as const object:常數物件 + 衍生 union(兩者兼得)
const Color = { Red: 'RED', Green: 'GREEN' } as const;
type Color = typeof Color[keyof typeof Color]; // 'RED' | 'GREEN'
ts

as const 寫法兼具「可像 enum 一樣用 Color.Red 取值」與「衍生出 union 型別」,又不犧牲 runtime 體積,是目前社群的主流推薦。

需求建議
只需要一組合法字串/數字、不需要 runtime 物件Union type ('a' | 'b')
需要像 Color.Red 一樣取值,又要 union 型別、又在意體積as const object
需要反向對映(值 → 名稱)數字 enum(唯一原生支援的)
既有專案大量使用、團隊習慣維持 enum 即可,不必硬改
面試一句話總結
Tuple 解決「固定長度 + 每位置型別」;Enum 解決「命名常數集合」,但因為有 runtime 成本與 const enum 的隔離編譯雷,現代多用 as const / union 取代——除非你需要數字 enum 的反向對映。

目錄

    ◆ 相關文章

    • Async function-Await 函式

      2026-06-02
    • throw Error用法

      2026-06-02
    • TypeScript 特性 - Interface

      2026-06-02
    • 箭頭函數 Arrow function expression

      2026-06-02
    ← 上一篇TypeScript 特性 - Interface下一篇 →箭頭函數 Arrow function expression

    ◆ 關於作者

    Kurau

    個人寫作 / 創作的 SoT,記錄遊戲、影視、學習與生活。

    更多 Kurau 的文章