彻底搞懂 TypeScript 数组与元组

从开发痛点到实战:彻底搞懂 TypeScript 数组与元组

一、数组(Array):同类数据

先思考一个场景:你需要存储一个班级所有学生的成绩,这些数据的特点是"类型相同(都是数字)、数量不固定(可能新增转学生)"。这时,数组就是最佳选择。

1. 核心定位:动态的同类型集合

数组的核心作用是存储相同类型的有序数据,长度可以动态调整(增删元素),就像一个可以无限扩容的收纳盒,专门用来装"同类物品"。TypeScript 通过类型标注强制数组内元素类型统一,避免混入其他类型导致错误。

2. 案例:解决同类数据存储问题

① 基础用法:明确类型的数组

最常用的两种类型标注方式:类型[]Array<类型>,效果完全一致,前者更简洁。

ts 复制代码
// 方式1:类型[](推荐)
const scores: number[] = [90, 85, 95, 88];
// 支持动态增删
scores.push(92); // 新增成绩
scores.pop(); // 删除最后一个成绩
console.log(scores.length); // 输出:4

// 方式2:Array<类型>(泛型写法)
const studentNames: Array<string> = ["张三", "李四", "王五"];
// 数组方法有完整类型提示
const filteredNames = studentNames.filter(name => name.length > 2);
console.log(filteredNames); // 输出:["张三"]
② 慎用场景:混合类型的 any 数组

有些同学会用 Array<any> 存储不同类型的数据,但这会完全丧失 TypeScript 的类型校验能力,相当于"放弃治疗",仅在兼容旧 JS 代码的特殊场景临时使用。

ts 复制代码
// 不推荐!失去类型安全,TS 无法校验元素类型
const mixedData: any[] = [123, "hello", true, { id: 1 }];
// 取值时无类型提示,容易出错
const num = mixedData[0] + 10; // 没问题
const str = mixedData[1].toUpperCase(); // 没问题
const err = mixedData[2].slice(0, 1); // 运行时错误:boolean 没有 slice 方法
③ 进阶用法:多维数组(嵌套数组)

当需要存储"同类数据的集合的集合"时,就需要用到多维数组,比如矩阵、表格数据。

ts 复制代码
// 二维数字数组:表示一个 3x3 的矩阵
const matrix: number[][] = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

// 访问二维数组元素:行索引 + 列索引
console.log(matrix[1][2]); // 输出:6(第二行第三列)

// 遍历二维数组
matrix.forEach(row => {
  const sum = row.reduce((acc, curr) => acc + curr, 0);
  console.log(`行总和:${sum}`);
});

二、元组(Tuple):异构数据的"固定结构包"

再看一个场景:你需要存储一个用户的核心信息------姓名(字符串)、年龄(数字)、是否VIP(布尔值)。这些数据的特点是"类型不同、数量固定",用数组存储会丢失类型语义,用对象又略显繁琐。这时,元组就能完美解决。

1. 核心定位:固定长度的异构组合

元组是 TypeScript 对 JavaScript 数组的扩展,核心作用是存储不同类型的有序数据,且长度固定(定义时约定多少个元素,就必须有多少个元素)每个索引位置的类型都是明确约定的,顺序具有严格语义。

2.案例:解决异构数据结构化问题

① 基础用法:固定结构的元组

元组的类型标注格式为 [类型1, 类型2, ...],每个位置的类型可以不同,且必须严格匹配。

ts 复制代码
// 元组:[姓名(string), 年龄(number), 是否VIP(boolean)]
const user: [string, number, boolean] = ["张三", 25, true];
console.log(user[0]); // 输出:张三
console.log(user[1]); // 输出:25

// 错误示例:类型不匹配或长度不符都会报错
// user = ["李四", "30", true]; // 报错:第二个元素应为 number
// user = ["王五", 30]; // 报错:长度应为 3
② 使用场景:函数返回多个不同类型值

JavaScript 函数默认只能返回一个值,要返回多个值通常需要用对象包裹。而元组可以更简洁地表达多返回值的类型,配合解构赋值。

ts 复制代码
// 函数返回元组:[是否成功(boolean), 数据(string[]), 错误信息(string)]
function fetchUserList(): [boolean, string[], string] {
  try {
    // 模拟接口请求成功
    const data = ["张三", "李四", "王五"];
    return [true, data, ""];
  } catch (error) {
    // 模拟接口请求失败
    return [false, [], "请求失败:网络错误"];
  }
}

// 解构赋值:语义清晰,类型提示完整
const [success, userList, errorMsg] = fetchUserList();
if (success) {
  console.log("用户列表:", userList);
} else {
  console.error(errorMsg);
}
③ 进阶用法:元组类型别名 + 元组数组

当需要批量处理多个相同结构的元组时,可以先定义元组类型别名,再创建元组数组,大幅提升代码可读性和可维护性。

ts 复制代码
// 定义用户接口
interface User {
  name: string;
  age: number;
}

// 模拟异步获取用户数据的函数
function fetchUsers(): Promise<User[]> {
  return new Promise((resolve, reject) => {
    const success = true; // 假设请求成功
    if (success) {
      const users: User[] = [
        { name: "张三", age: 18 },
        { name: "李四", age: 15 },
        { name: "王五", age: 25 },
      ];
      resolve(users);
    } else {
      reject("请求失败:网络错误");
    }
  });
}

// 函数返回用户信息和状态
async function loadAdultUsers(): Promise<[boolean, User[], string]> {
  try {
    const users = await fetchUsers();
    // 过滤出成年用户(年龄大于等于18)
    const adultUsers = users.filter(user => user.age >= 18);
    return [true, adultUsers, ""];
  } catch (error) {
    return [false, [], error as string];
  }
}

// 使用示例
(async () => {
  const [success, adultUsers, errorMsg] = await loadAdultUsers();
  if (success) {
    console.log("成年用户列表:", adultUsers);
  } else {
    console.error(errorMsg);
  }
})();

三、核心差异

混淆数组和元组,本质是没抓住核心差异。下面这张表从 6 个关键维度对比,一看就懂:

对比维度 数组(Array) 元组(Tuple)
长度特性 动态可变,支持增删元素 固定长度,与定义时的类型数量严格匹配
元素类型 推荐同类型(异构需用 any,丧失类型安全) 支持异构类型,每个索引类型可自定义
类型安全 仅校验元素类型,不校验长度 同时校验元素类型和长度,安全性更高
语义表达 表示"同类事物的列表"(如所有学生成绩、所有商品名称) 表示"不同类型数据的组合"(如姓名+年龄、是否成功+数据)
使用成本 低,上手简单,无需关注索引语义 中,需提前约定索引语义,避免顺序错乱
扩展能力 强,支持数组所有原生方法(map、filter、reduce 等) 弱,本质是数组,但固定长度限制了部分扩展场景

核心结论:数组的核心是"动态+同类",元组的核心是"固定+异构"。记住这八个字,就能快速判断该用哪个。

相关推荐
weixin_584121435 小时前
vue内i18n国际化移动端引入及使用
前端·javascript·vue.js
imkaifan5 小时前
bind函数--修改this指向,返回一个函数
开发语言·前端·javascript·bind函数
xkxnq5 小时前
第一阶段:Vue 基础入门(第 7 天)
前端·javascript·vue.js
光头闪亮亮5 小时前
企业协同办公系统(OA)-【图标选择器】模块开发详解
前端·javascript·vue.js
pas1365 小时前
22-mini-vue props
前端·javascript·vue.js
pas1365 小时前
23-mini-vue 实现 emit 功能
前端·javascript·vue.js
cute_ming5 小时前
从 Node.js + TypeScript 无缝切换到 Python 的最佳实践
python·typescript·node.js
黛色正浓5 小时前
leetCode-热题100-子串合集(JavaScript)
javascript·算法·leetcode
程序员Agions5 小时前
React 自定义 Hooks 生存指南:7 个让你少加班的"偷懒"神器
前端·javascript
爱学习的小康5 小时前
js 文件读取 修改 创建
前端·javascript·vue.js