TypeScript 基础类型(下):数组、元组与枚举

本文献给:

已掌握 JavaScript 基础,并熟悉 TypeScript 中 string、number、boolean 及类型注解的开发者。本文将系统讲解 TypeScript 中数组、元组与枚举的定义与使用,帮助你精准描述更复杂的数据结构。

你将学到:

  1. 数组的三种定义方式与类型推断
  2. 元组的固定长度、可选元素与剩余元素
  3. 数字枚举、字符串枚举及常量枚举的区别
  4. 枚举编译后的 JavaScript 产物分析
  5. 常见错误与最佳实践

目录

  • 一、数组类型(Array)
    • [1.1 基本定义](#1.1 基本定义)
    • [1.2 数组的类型推断](#1.2 数组的类型推断)
    • [1.3 只读数组](#1.3 只读数组)
    • [1.4 多维数组](#1.4 多维数组)
  • 二、元组(Tuple)
    • [2.1 基本元组](#2.1 基本元组)
    • [2.2 可选元素](#2.2 可选元素)
    • [2.3 剩余元素(Rest Elements)](#2.3 剩余元素(Rest Elements))
    • [2.4 只读元组](#2.4 只读元组)
    • [2.5 元组与数组的区别](#2.5 元组与数组的区别)
  • 三、枚举(Enum)
    • [3.1 数字枚举](#3.1 数字枚举)
    • [3.2 字符串枚举](#3.2 字符串枚举)
    • [3.3 异构枚举(混合)](#3.3 异构枚举(混合))
    • [3.4 常量枚举(const enum)](#3.4 常量枚举(const enum))
    • [3.5 枚举编译产物分析](#3.5 枚举编译产物分析)
    • [3.6 枚举与字面量联合类型对比](#3.6 枚举与字面量联合类型对比)
  • 四、常见错误与注意事项
    • [4.1 数组赋值类型不匹配](#4.1 数组赋值类型不匹配)
    • [4.2 元组越界访问](#4.2 元组越界访问)
    • [4.3 枚举的陷阱:数字枚举反向映射污染](#4.3 枚举的陷阱:数字枚举反向映射污染)
    • [4.4 常量枚举与 `isolatedModules` 不兼容](#4.4 常量枚举与 isolatedModules 不兼容)
    • [4.5 `as const` 与枚举的选择](#4.5 as const 与枚举的选择)
  • 五、综合示例
  • 六、小结

一、数组类型(Array)

在 TypeScript 中,数组类型有两种等效的写法:类型 + 方括号泛型 Array<类型>

1.1 基本定义

typescript 复制代码
// 方式一:类型 + []
let list1: number[] = [1, 2, 3];
let list2: string[] = ['a', 'b', 'c'];

// 方式二:泛型 Array<类型>
let list3: Array<number> = [1, 2, 3];
let list4: Array<string> = ['a', 'b', 'c'];

两种方式完全等价,推荐使用 类型[],更简洁易读。

1.2 数组的类型推断

初始化数组时,TypeScript 会根据元素类型推断数组类型:

typescript 复制代码
let arr1 = [1, 2, 3];        // 推断为 number[]
let arr2 = ['hello', 'ts'];  // 推断为 string[]
let arr3 = [1, 'hello'];     // 推断为 (string | number)[]
let arr4 = [];               // 推断为 any[](需谨慎)

如果数组初始为空,后续推入元素也会影响类型推断:

typescript 复制代码
let arr = [];      // any[]
arr.push(1);       // 此时 arr 仍然是 any[]
arr.push('a');     // 仍然允许

// 更好的做法:显式注解
let arr2: number[] = [];
arr2.push(1);      // OK
arr2.push('a');    // ❌

1.3 只读数组

有时我们希望数组创建后不可修改,可以使用 readonlyReadonlyArray<T>

typescript 复制代码
// 方式一:readonly 修饰符
let roArr: readonly number[] = [1, 2, 3];
roArr[0] = 10;      // ❌ 无法赋值
roArr.push(4);      // ❌ 属性 push 不存在

// 方式二:ReadonlyArray<T> 泛型
let roArr2: ReadonlyArray<number> = [1, 2, 3];

// 方式三:使用 as const(更严格,变成字面量只读元组)
let roArr3 = [1, 2, 3] as const;
// roArr3 类型为 readonly [1, 2, 3]

提示readonly number[]ReadonlyArray<number> 等价,但前者更简洁。

1.4 多维数组

typescript 复制代码
let matrix: number[][] = [
    [1, 2, 3],
    [4, 5, 6]
];

let deep: string[][][] = [[['a']]];  // 三维数组

二、元组(Tuple)

元组允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

2.1 基本元组

typescript 复制代码
let tuple: [string, number] = ['Alice', 25];
// 类型顺序必须匹配
let wrong: [string, number] = [25, 'Alice']; // ❌

访问元素时,TypeScript 会正确推断对应位置类型:

typescript 复制代码
let name = tuple[0];  // string
let age = tuple[1];   // number

2.2 可选元素

在元组中可以使用 ? 标记可选元素,可选元素必须位于必选元素之后。

typescript 复制代码
let tuple2: [string, number?] = ['Alice'];     // OK
let tuple3: [string, number?] = ['Alice', 25]; // OK
let tuple4: [string, number?] = [25];          // ❌ 第一个必须是 string

2.3 剩余元素(Rest Elements)

使用 ... 定义不定长度的元组,剩余元素必须是数组类型。

typescript 复制代码
// 至少一个 string,后面任意多个 number
let tuple5: [string, ...number[]] = ['Alice', 1, 2, 3];

// 开头任意多个 boolean,最后一个是 string
let tuple6: [...boolean[], string] = [true, false, 'end'];

// 混合使用:string, 可选 number, 任意多个 boolean
let tuple7: [string, number?, ...boolean[]] = ['start', true, false];

2.4 只读元组

typescript 复制代码
let roTuple: readonly [string, number] = ['Alice', 25];
roTuple[0] = 'Bob';   // ❌ 只读属性

// 或者使用 as const
let roTuple2 = ['Alice', 25] as const;
// 类型为 readonly ["Alice", 25]

2.5 元组与数组的区别

特性 数组 number[] 元组 [string, number]
元素类型 全部相同 可以不同
长度 可变 固定(除剩余元素外)
常见场景 列表、集合 固定结构(如坐标、键值对)

三、枚举(Enum)

枚举是 TypeScript 对 JavaScript 的扩展,用于定义一组命名常量,使代码更易读。

3.1 数字枚举

数字枚举的成员默认从 0 开始自增。

typescript 复制代码
enum Direction {
    Up,     // 0
    Down,   // 1
    Left,   // 2
    Right   // 3
}

let dir: Direction = Direction.Up;
console.log(dir);  // 0

可以手动赋值:

typescript 复制代码
enum Status {
    Pending = 1,
    Approved,    // 2(自动递增)
    Rejected     // 3
}

// 部分手动,部分自动
enum Color {
    Red = 1,
    Green,   // 2
    Blue = 10,
    Yellow   // 11
}

数字枚举支持反向映射(通过值获取键名):

typescript 复制代码
enum Direction {
    Up,
    Down,
    Left,
    Right
}

console.log(Direction[0]);  // "Up"
console.log(Direction.Up);  // 0

3.2 字符串枚举

字符串枚举的每个成员必须用字符串字面量初始化,没有自增行为。

typescript 复制代码
enum LogLevel {
    Error = "ERROR",
    Warn = "WARN",
    Info = "INFO"
}

let level: LogLevel = LogLevel.Error;
console.log(level);  // "ERROR"

字符串枚举不支持反向映射

3.3 异构枚举(混合)

不推荐使用,除非有特殊需求。

typescript 复制代码
enum Mixed {
    No = 0,
    Yes = "YES"
}

3.4 常量枚举(const enum)

常量枚举在编译时会被内联,不生成真实的对象代码,性能更好。

typescript 复制代码
const enum Direction {
    Up,
    Down,
    Left,
    Right
}

let move = Direction.Up;

编译后:

javascript 复制代码
// 编译结果
let move = 0 /* Direction.Up */;

常量枚举的限制 :不能使用反向映射,且只能在外部模块中访问(如果 preserveConstEnums 为 false,默认如此)。

3.5 枚举编译产物分析

普通数字枚举

typescript 复制代码
// TypeScript
enum Color {
    Red,
    Green,
    Blue
}

编译后的 JavaScript:

javascript 复制代码
// JavaScript
var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));

生成的 Color 对象同时包含正向映射(Color.Red -> 0)和反向映射(Color[0] -> "Red")。

字符串枚举

typescript 复制代码
enum LogLevel {
    Error = "ERROR",
    Warn = "WARN"
}

编译后:

javascript 复制代码
var LogLevel;
(function (LogLevel) {
    LogLevel["Error"] = "ERROR";
    LogLevel["Warn"] = "WARN";
})(LogLevel || (LogLevel = {}));

没有反向映射。

常量枚举

常量枚举在编译后完全消失,所有引用被替换为字面量值。

typescript 复制代码
const enum Size {
    Small = 1,
    Large = 2
}
let s = Size.Small;

编译后:

javascript 复制代码
let s = 1;  // 直接替换为值

3.6 枚举与字面量联合类型对比

现代 TypeScript 推荐使用字面量联合类型替代简单枚举,尤其是字符串枚举。

typescript 复制代码
// 枚举方式
enum Direction {
    Up = "UP",
    Down = "DOWN"
}

// 字面量联合类型(更轻量,无运行时开销)
type Direction = "UP" | "DOWN";
let dir: Direction = "UP";

选择建议

  • 需要反向映射、自增行为或运行时枚举对象 → 使用数字枚举
  • 仅需一组常量字符串,且不关心运行时 → 使用字面量联合类型
  • 需要将枚举值作为独立类型使用,并且不希望产生额外代码 → 使用 const enum(但注意打包工具兼容性)

四、常见错误与注意事项

4.1 数组赋值类型不匹配

typescript 复制代码
let nums: number[] = [1, 2, 3];
nums = ['a', 'b'];   // ❌ string 不能赋给 number

4.2 元组越界访问

typescript 复制代码
let tuple: [string, number] = ['Alice', 25];
let val = tuple[2];   // ❌ 长度为 2,索引 2 越界

4.3 枚举的陷阱:数字枚举反向映射污染

typescript 复制代码
enum Status {
    Active = 1,
    Inactive
}
// 编译后,Status 对象额外包含反向映射键,可能干扰对象遍历

4.4 常量枚举与 isolatedModules 不兼容

在使用 ts-loaderbabel 且开启 isolatedModules 时,常量枚举可能出错。此时需要改用普通枚举或禁用常量枚举。

4.5 as const 与枚举的选择

typescript 复制代码
// as const 生成只读元组/对象,没有独立类型名
const colors = ['red', 'green', 'blue'] as const;  // 类型为 readonly ["red", "green", "blue"]

// 需要重复使用类型别名时,更适合用枚举或字面量联合
type Color = 'red' | 'green' | 'blue';

五、综合示例

typescript 复制代码
// 定义一个学生元组:[姓名, 年龄, 可选性别]
type Student = [string, number, 'male' | 'female'?];

// 定义一个班级类
class ClassRoom {
    private students: Student[] = [];

    addStudent(...student: Student) {
        this.students.push(student);
    }

    getAllStudents(): Student[] {
        return this.students;
    }

    // 枚举用于成绩等级
    getGrade(score: number): Grade {
        if (score >= 90) return Grade.A;
        if (score >= 75) return Grade.B;
        if (score >= 60) return Grade.C;
        return Grade.D;
    }
}

// 数字枚举
enum Grade {
    A = 90,
    B = 75,
    C = 60,
    D = 0
}

// 使用示例
const classroom = new ClassRoom();
classroom.addStudent('Alice', 20, 'female');
classroom.addStudent('Bob', 22);  // 性别可选

console.log(classroom.getGrade(85));  // 输出 75(Grade.B)

// 遍历学生
for (const [name, age, gender] of classroom.getAllStudents()) {
    console.log(`${name}, ${age}岁${gender ? `, 性别 ${gender}` : ''}`);
}

六、小结

类型 语法示例 特点
数组 let arr: number[] = [1,2] 同类型,长度可变
只读数组 let ro: readonly number[] = [1] 不可修改
元组 let t: [string, number] = ['a',1] 固定长度,类型可不同
可选元组 let t: [string, number?] = ['a'] 元素可选
剩余元组 let t: [string, ...number[]] 不定长尾部
数字枚举 enum E { A, B } 自动递增,支持反向映射
字符串枚举 enum E { A = "a", B = "b" } 无反向映射
常量枚举 const enum E { A, B } 编译时内联,无运行时对象

觉得文章有帮助?别忘了:

👍 点赞 👍 -- 给我一点鼓励
⭐ 收藏 ⭐ -- 方便以后查看
🔔 关注 🔔 -- 获取更新通知


标签: #TypeScript #数组 #元组 #枚举 #学习笔记 #前端开发

相关推荐
小茴香3532 小时前
拖拽实现(原生JS+Vue)
前端·javascript·vue.js·typescript
吴声子夜歌15 小时前
TypeScript——泛型
前端·git·typescript
吴声子夜歌19 小时前
TypeScript——类型基础(二)
linux·ubuntu·typescript
2301_8227828221 小时前
C语言数组通关攻略!从一维到字符数组,零基础也能轻松掌握
c语言·算法·数组·编程基础·避坑技巧
Wect1 天前
LeetCode 215. 数组中的第K个最大元素:大根堆解法详解
前端·算法·typescript
必然秃头1 天前
下篇:TypeScript 高级特性(高级类型、类型操作、最佳实践)
typescript
吴声子夜歌1 天前
TypeScript——内置工具类型、类型查询、类型断言和类型细化
linux·ubuntu·typescript
楚轩努力变强1 天前
2026 年前端进阶:端侧大模型 + WebGPU,从零打造高性能 AI 原生前端应用
前端·typescript·大模型·react·webgpu·ai原生·高性能前端
吴声子夜歌1 天前
TypeScript——索引类型、映射对象类型、条件类型
git·ubuntu·typescript