数组类型
本教程共 80 篇 · 第 10 篇 · 更新于 2026-08-10 · 约 8 分钟阅读
本节目标:学会在 TypeScript 中声明和操作类型安全的数组,理解两种数组写法的等价性,掌握只读数组的作用,为下一章的元组做好铺垫。
数组为什么需要类型
JavaScript 的数组可以装任何东西——数字、字符串、对象混在一起都没问题。这在灵活性上是好事,但也是 bug 的来源。你本来期望 scores 数组里全是数字,结果某个地方不小心塞了个字符串,等到排序或求和时才炸。
TypeScript 的数组类型解决的就是这个问题:数组里每个元素的类型都是已知的,编译器帮你守住这条线。
let scores: number[] = [95, 88, 76, 100];
// scores.push("九十"); // ❌ 类型 "string" 不能赋给 number[]
scores.push(92); // ✅
两种写法:T[] 和 Array<T>
TypeScript 支持两种语法来声明数组类型:
// 写法一:T[](更常见,推荐)
let numbers: number[] = [1, 2, 3];
let names: string[] = ["小明", "小红", "小刚"];
// 写法二:Array<T>(泛型写法)
let numbers2: Array<number> = [1, 2, 3];
let names2: Array<string> = ["小明", "小红", "小刚"];
两种写法完全等价,编译结果也一样。区别在于风格偏好:number[] 更简短,在社区中更流行;Array<number> 跟其他泛型语法更统一。
Tip日常开发中推荐用
T[],它读起来更自然——“number 数组”而不是”数字的数组”。但在需要嵌套泛型时(比如Array<Array<number>>),Array<T>写法更容易看清楚结构。
如果你的数组混合了不同类型,TypeScript 会推断为联合类型数组:
let mixed = [1, "hello", 3]; // 推断为 (string | number)[]
这种推断有时候不是你想要的。如果你本意是 number[] 却混入了字符串,请检查一下数据来源。
多维数组
多维数组就是”数组的数组”,类型写法是类型的嵌套:
// 二维数字数组(矩阵)
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 等价于
let matrix2: Array<Array<number>> = [
[1, 2, 3],
[4, 5, 6]
];
// 三维数组
let cube: number[][][] = [
[[1, 2], [3, 4]],
[[5, 6], [7, 8]]
];
// 访问元素
let value: number = matrix[1][2]; // 6
多维数组中每一维的类型检查都有效:
// matrix[0] = "wrong"; // ❌ string[] 不能赋给 number[]
// matrix[0][0] = "wrong"; // ❌ string 不能赋给 number
只读数组 ReadonlyArray<T>
有时候你需要保证数组不被修改——比如一个配置常量列表、一组预定义的选项。TypeScript 提供了只读数组类型:
let colors: readonly string[] = ["red", "green", "blue"];
// 所有修改操作都被禁止
// colors[0] = "yellow"; // ❌ 索引赋值不允许
// colors.push("purple"); // ❌ push 不存在于 readonly string[]
// colors.pop(); // ❌ pop 也不允许
// colors.splice(0, 1); // ❌ splice 也不允许
// 读取操作完全正常
console.log(colors[0]); // "red"
console.log(colors.length); // 3
console.log(colors.includes("green")); // true
readonly string[] 等价于 ReadonlyArray<string>:
let colors2: ReadonlyArray<string> = ["red", "green", "blue"];
// colors2.push("purple"); // ❌ push 不存在
只读数组的一个关键行为:不能把只读数组赋值给可变数组。反过来也不行:
let mutable: string[] = ["a", "b"];
let readonly: readonly string[] = ["c", "d"];
// mutable = readonly; // ❌ readonly string[] 不能赋给 string[]
readonly = mutable; // ✅ 可变可以赋给只读
这个单向兼容设计保证了安全性——可变数组承诺”我会保持可修改”,只读数组做不到。
数组方法的类型推断
TypeScript 对数组方法的类型推断非常精准。以 map、filter 为例:
let numbers = [1, 2, 3, 4, 5];
// map:TS 知道每个元素是 number,返回值是 number[]
let doubled = numbers.map((n) => n * 2); // doubled: number[]
// filter:返回值仍是 number[](虽然元素变少了)
let evens = numbers.filter((n) => n % 2 === 0); // evens: number[]
// 复杂一点的:map 返回不同类型的数组
let labels = numbers.map((n) => `第 ${n} 个`); // labels: string[]
使用 find 时要注意返回是 T | undefined——因为可能找不到:
let numbers = [1, 2, 3, 4, 5];
let found = numbers.find((n) => n > 10);
// found 的类型是 number | undefined
// 需要检查才能安全使用
if (found !== undefined) {
console.log(found * 2); // ✅ 类型安全
}
数组 vs 元组:一个预告
数组和元组长得像,但语义不同:
// 数组:长度可变,所有元素同类型
let scores: number[] = [95, 88, 76];
scores.push(100); // ✅ 可以添加
// 元组:长度固定,每个位置类型可以不同
let person: [string, number] = ["小明", 18];
// person.push(true); // ❌ 元素类型和位置都受限
简单粗暴的区分:数组是”一堆同样的东西”,元组是”几个固定位置的东西”。下一章专门讲元组,这里先混个脸熟。
可运行示例
// 数组类型综合示例
// 1. 基本数组操作
let students: string[] = ["小明", "小红", "小刚", "小李"];
console.log(`班级共 ${students.length} 人`);
// 2. map 和 filter
let scores: number[] = [95, 88, 76, 92, 60];
let passingScores = scores.filter((s) => s >= 60);
let gradeLabels = passingScores.map((s) => {
if (s >= 90) return "优";
if (s >= 75) return "良";
return "及格";
});
console.log("通过人数:", passingScores.length);
console.log("等级:", gradeLabels);
// 3. 只读数组
const DAYS: readonly string[] = ["周一", "周二", "周三", "周四", "周五"];
console.log("工作日:", DAYS.join("、"));
// DAYS.push("周六"); // ❌ 只读数组不能修改
// 4. 二维数组
let grid: number[][] = [
[1, 2, 3],
[4, 5, 6]
];
console.log("第二行第三列:", grid[1][2]); // 6
输出:
班级共 4 人
通过人数: 5
等级: [ "优", "良", "良", "优", "及格" ]
工作日: 周一、周二、周三、周四、周五
第二行第三列: 6
小结
数组类型是 TypeScript 中使用频率仅次于基础类型的类型。两种写法 T[] 和 Array<T> 效果一样,选自己顺手的。只读数组在不想让数据被意外修改时很有用。下一章我们看元组——当数组中每个位置的元素类型各不相同时,元组是最佳选择。