TypeScript 数组与元组
上一节我们学习了基础类型,但实际开发中很少只处理单个值,我们经常需要处理一组数据,比如用户列表、商品标签、坐标点等。
TypeScript 提供了数组和元组两种方式来描述这类数据结构。
一句话先记住区别:
- 数组:一组相同类型的数据,长度不固定
- 元组:一组不同类型的数据,长度和位置固定
数组:相同类型的集合
定义方式
TypeScript 中定义数组有两种方式,推荐使用第一种,更简洁。
// 方式一:类型 + 方括号(推荐)
let names: string[] = ["张三", "李四", "王五"];
let ages: number[] = [18, 20, 25];
// 方式二:泛型写法 Array<类型>
let names2: Array<string> = ["张三", "李四", "王五"];
let ages2: Array<number> = [18, 20, 25];
类型检查
添加了类型注解后,TypeScript 会阻止你放入错误类型的数据:
let names: string[] = ["张三", "李四"];
names.push("王五"); // ✅ 正确
names.push(123); // ❌ 报错:不能将 number 赋值给 string
names[0] = 100; // ❌ 报错
类型推断
如果省略类型注解,TypeScript 会根据初始值自动推断数组类型:
// 没有写类型注解,但 TypeScript 自动推断为 string[]
let fruits = ["苹果", "香蕉", "橙子"];
// 此时再推入数字会报错
fruits.push(123); // ❌ 报错
混合类型数组
如果数组需要同时包含多种类型,可以使用联合类型(后续章节会详细介绍):
// 数组里可以是 string 或 number
let mixed: (string | number)[] = ["张三", 25, "李四", 30];
数组常用操作
TypeScript 中的数组方法(map、filter、push 等)都会自动保留类型信息:
const numbers: number[] = [1, 2, 3, 4, 5];
// map 返回的仍然是 number[]
const doubled = numbers.map(n => n * 2); // 推断为 number[]
// filter 返回的仍然是 number[]
const evens = numbers.filter(n => n % 2 === 0); // 推断为 number[]
// 处理混合类型时,配合类型保护更安全
const items: (string | number)[] = ["apple", 1, "banana", 2];
const strings = items.filter((item): item is string => typeof item === "string"); // 推断为 string[]
元组:固定结构的组合
元组是 TypeScript 特有的类型(JavaScript 没有),它用于表示元素数量和类型都固定的数据组合。
定义方式
// 定义元组:第一位是 string,第二位是 number,第三位是 boolean
let person: [string, number, boolean] = ["张三", 25, true];
// 访问元组元素
console.log(person[0]); // "张三"(类型为 string)
console.log(person[1]); // 25(类型为 number)
严格约束
元组对长度和每个位置的类型都有严格要求:
let person: [string, number] = ["张三", 25];
// ❌ 以下写法都会报错
person = ["张三"]; // 缺少第二个元素
person = ["张三", 25, true]; // 元素过多
person[0] = 100; // 类型错误:不能将 number 赋值给 string
可选元素
元组中的某些元素可以是可选的(用 ? 标记),适合部分字段有值的情况:
// 第三个元素可选
let user: [string, number, boolean?] = ["张三", 25];
user = ["李四", 30, true]; // ✅ 也可以
// 访问可选的第三个元素
console.log(user[2]); // 类型为 boolean | undefined
实际使用场景
场景一:坐标点
// 经纬度 [经度, 纬度]
let location: [number, number] = [116.4, 39.9];
function getLocation(): [number, number] {
return [116.4, 39.9];
}
场景二:API 响应(状态码 + 数据)
type ApiResponse = [number, string | null];
function fetchUser(): ApiResponse {
if (Math.random() > 0.5) {
return [200, "用户数据"];
} else {
return [500, null];
}
}
const [code, data] = fetchUser();
if (code === 200) {
console.log("成功:", data);
}
场景三:键值对
// 配置项 [键, 值]
let config: [string, string] = ["theme", "dark"];
常见问题
TypeScript 允许元组使用 push 方法添加元素,但这会破坏元组的长度约束!
let person: [string, number] = ["张三", 25];
person.push("extra"); // ⚠️ TypeScript 不会报错!
// 但 person 实际变成了 ["张三", 25, "extra"]
// 违背了元组的初衷
原因:元组在编译后变成普通数组,push 方法存在于数组原型上,TypeScript 无法禁止调用。
建议:不要把元组当作数组使用,不要对元组调用 push / pop 等方法。
数组 vs 元组:对比总结
| 对比维度 | 数组 | 元组 |
|---|---|---|
| 元素类型 | 必须统一 | 可以各不相同 |
| 长度 | 不固定,可增删 | 固定,不可变 |
| 使用场景 | 列表数据 | 固定结构的数据组合 |
| 示例 | 用户列表、标签集合 | 坐标 [x, y]、API 响应 [code, data] |
选型建议:
- 数据是同类型的列表(如多个用户、多个商品)→ 用 数组
- 数据是固定结构的组合(如经纬度、键值对)→ 用 元组