数组、元组与枚举

本节目标

数组有两种写法,等价:

// 运行环境:Node.js 16+ + typescript;类型检查 npx tsc --noEmit ts-l4.ts
let nums: number[] = [1, 2, 3]
let nums2: Array<number> = [1, 2, 3] // 泛型写法,和上一行等价

// 数组里放错类型直接报错
// nums.push('x') // ❌ Argument of type 'string' is not assignable to 'number'

**元组(Tuple)**是「长度固定、每个位置类型已知」的数组,适合表达「有明确结构的一对小值」:

// 表示「用户名 + 年龄」这样一对固定结构
let user: [string, number] = ['Laowen', 30]
// user = ['Laowen']        // ❌ 少一个元素报错
// user = [30, 'Laowen']    // ❌ 顺序错了也报错

// 实际应用:React 的 useState 返回的就是 [值, 设值函数] 元组
function useFakeState<T>(init: T): [T, (v: T) => void] {
  let value = init
  const setter = (v: T) => { value = v }
  return [value, setter]
}
const [name, setName] = useFakeState('guest')
console.log(name) // guest

**枚举(enum)**给一组相关常量起名字:

// 用 enum 表达「订单状态」
enum OrderStatus {
  Pending = 'pending',
  Paid = 'paid',
  Shipped = 'shipped',
}
const status: OrderStatus = OrderStatus.Paid
console.log(status) // 'paid'

⚠️ 坑:enum 在编译后会生成一段「运行时代码」(不是纯类型),而且默认数字枚举会「反向映射」。现代 TS 更推荐用 as const 对象或联合类型替代枚举(见下一节),更轻量、更透明。

名词解释

元组(Tuple):一种「定长且每格类型固定」的数组。普通数组 number[] 要求每个元素都是 number 且个数不限;元组 [string, number] 要求「第 0 个是 string、第 1 个是 number、且只有 2 个」。它适合表达「成对出现的结构化数据」,好比一张两列的表格的一行。

枚举(Enum):把一组「有意义的名字」映射到「值」的工具,让代码里不出现魔法字符串/数字。比如用 OrderStatus.Paid 比直接写 'paid' 更安全、更易读。但要记住它会在运行时真实存在。

课后练习

练习 1:用元组表示一个「经纬度坐标 [number, number]」,并写一个函数判断它是否在原点。

答案:

type Point = [number, number]
function isOrigin(p: Point): boolean {
  return p[0] === 0 && p[1] === 0
}

练习 2:既然 enum 有运行时开销,如何用「联合类型 + as const」表达订单状态更安全?

答案:

const OrderStatus = {
  Pending: 'pending',
  Paid: 'paid',
  Shipped: 'shipped',
} as const
type OrderStatus = typeof OrderStatus[keyof typeof OrderStatus] // 'pending' | 'paid' | 'shipped'

总结

我认为元组是被低估的类型:凡是「两个或三个相关值、且位置有意义」的场景(坐标、键值对、useState 返回值),用元组比用对象更简洁,而且解构赋值读起来很舒服。至于枚举,我的态度是能不用就不用——数字枚举的反向映射和运行时体积常常带来惊喜(贬义)。现在社区主流做法是用 as const 对象 + 从它推导出的联合类型,零运行时成本、IDE 提示还一样好。只有在和既有 JS/第三方库强绑定时,才退而用 enum。