在 JavaScript 中,数组是一个非常灵活的容器。对于一个数组来说,我们不仅可以往里面放数字、字符串,甚至可以把对象放进去。
示例 1:传统的 JavaScript 数组
const arr = [1, 2, "绿叶网", true];
console.log(arr[2]);运行结果如下。
绿叶网分析:
虽然数组这种灵活性在某些场景下很方便,但在大型项目中,这往往是 bug 的温床。比如我们本以为数组里全是数字并准备进行加法运算,结果里面突然冒出一个字符串,然后导致程序崩溃了
在 TypeScript 中,我们可以使用 Array 类型来对数组进行约束,以限制它只能存放某种类型的数据。
TypeScript 定义数组
在 TypeScript 中,定义数组主要有两种语法,它们的效果是完全一样的,我们可以根据自己的喜好来选择。
1. 类型 + 方括号(推荐)
这是我们在实际开发中最常使用的一种方式,语法非常直观了然。
语法:
const 变量名: 类型[] = [值1, 值2, ...];说明:
示例 2:定义数字数组
// 只能存放 “数字”
const prices: number[] = [10, 20, 30];
// 只能存放 “字符串”
const colors: string[] = ["red", "green", "blue"];
console.log(prices[0]);
console.log(colors[1]);运行结果如下。
10
green分析:
在这个例子中,我们使用 “number[]” 来限制 prices 这个数组只能存放数字,并且使用 “string[]” 用于限制 colors 这个数组只能存放字符串。
2. 泛型数组
在 TypeScript 中,我们还可以使用 “泛型” 的写法来约束一个数组。它的效果和第一种方式是完全一样的,只是写法不同罢了。
语法:
const 变量名: Array<类型> = [值1, 值2, ...];说明:
关于泛型的详细使用,我们在后面会花整一章来介绍,这里简单了解即可。
示例 3:使用泛型定义数组
const prices: Array<number> = [10, 20, 30];
const colors: Array<string> = ["red", "green", "blue"];
console.log(prices[0]);
console.log(colors[1]);运行结果如下。
10
green分析:
在实际的 Vue 或 React 开发中,绝大多数情况下我们都会优先采用第一种 “类型[]” 的写法,因为它更加直观。
示例 4:越界类型的拦截
const ageArr: number[] = [18, 20, 22];
// 正常操作:向数组中追加一个数字
ageArr.push(25);
// 错误操作:试图向数字数组中追加一个字符串
ageArr.push("绿叶网");分析:
前面我们说了,TypeScript 是一位 “严谨的工程师”。一旦我们为数组指定了类型,任何试图破坏规则的操作都会被它拦截。
对于这个例子来说,当我们在编辑器中敲下 ageArr.push("绿叶网"); 这句代码时,编辑器立马就会飘红并提示错误:
类型 “string” 的参数不能赋给类型 “number” 的参数实际上,对于数组的所有内置方法(无论是增删改查还是遍历迭代),TypeScript 都提供了严格的类型监控和智能提示,常用的如下表所示。
| 增删查改 | |
| unshift() | 在数组开头添加元素,返回新长度 |
| push() | 在数组末尾添加元素,返回新长度 |
| shift() | 移除并返回数组的第一个元素 |
| pop() | 移除并返回数组的最后一个元素 |
| 排序与变换 | |
| reverse() | 反转数组元素顺序,修改原数组 |
| sort() | 对数组元素排序,修改原数组 |
| 遍历与衍生 | |
| filter() | 过滤数组,返回由符合条件元素组成的新数组 |
| map() | 映射数组,返回处理后的新数组 |
这种在编写阶段就把 Bug 扼杀在摇篮里的感觉,正是大多数前端开发者们偏爱 TypeScript 的原因。
TypeScript 对象数组
这里有小伙伴可能会问:“在真实项目开发时,后端传给我们的数据往往是非常复杂的,通常是一个包含了多个对象的数组。像这样的数据,使用 TypeScript 应该如何定义呢?”
在这种情况下,我们很少会只使用 “纯数字” 或 “纯字符串” 的数组,最常见使用的是 “对象数组”。此时,我们需要结合 “接口(interface)” 来对数组元素进行约束才行。
提示: 关于接口的使用,我们在后面 “TypeScript 接口” 一节中会详细介绍。这里简单了解一下即可。
示例 5:定义复杂的对象数组
// 先定义接口
interface Article {
id: number;
title: string;
isPublished: boolean;
}
// 再定义数组
const articleArr: Article[] = [
{
id: 1,
title: "TypeScript 数组",
isPublished: true
},
{
id: 2,
title: "TypeScript 元组",
isPublished: false
},
{
id: 3,
title: "TypeScript 接口",
isPublished: false
}
];
console.log(articleArr[0].title);运行结果如下。
TypeScript 数组分析:
在 Vue 或 React 项中,使用 interface 搭配 “类型[]” 来声明响应式列表数据,这种写法是最常用的。它能够确保你从后端接口拿到的庞大数据,在页面渲染中都不会出现 “字段找不到” 的低级错误。
TypeScript 多维数组
在一些特殊的业务场景下(比如做个小游戏、处理矩阵数据等),可能会需要使用二维甚至多维数组。
在 TypeScript 中,定义多维数组也是非常简单的,我们只需要多加几个 “[]” 即可。
示例 6:定义二维数组
const matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(matrix[1][1]);运行结果如下。
5分析:
在这个例子中,我们使用 “number[][]” 来定义了一个二维数组,该二维数组的元素要求是 “number(数字)”。
如果要定义一个纯数字的三维数组,则我们可以使用 “number[][][]” 来定义,以此类推。
TypeScript 只读数组
前面提到过,在 Vue 或 React 项目中,我们尽量使用 const 来定义变量。但需要注意的是,const 只能保证数组的 “引用地址” 不被修改,它并不能阻止我们去修改数组里面的元素(比如使用 push() 添加数据,或者直接修改 arr[0] = 100)。
在大型项目中,如果我们希望定义一个 “绝对安全、完全不可被修改” 的数组(比如固定配置项、常量列表),此时可以使用 TypeScript 提供的 readonly 修饰符。
示例 7:定义只读数组
// 定义只读数组
const roles: readonly string[] = ["admin", "editor", "guest"];
// 正常读取
console.log(roles[0]);
// 错误操作 1:试图修改内部元素
roles[1] = "super_admin";
// 报错:类型 “readonly string[]” 中的索引签名仅允许读取
// 错误操作 2:试图调用会改变原数组的方法
roles.push("user");
// 报错:类型 “readonly string[]” 上不存在属性“ push”分析:
一旦数组被标记为 readonly,那么TypeScript 就会立刻拦截所有可能改变数组自身的操作(包括 push()、pop()、splice() 等),从而从根源上保证了数据的纯洁性和安全性。这在 “单向数据流” 中,是非常有用的。
常见问题
1. 如果我们希望数组既可以存放数字,又可以存放字符串,应该怎么做呢?
这时我们可以使用后面介绍的 “TypeScript 联合类型” 来实现,使用一个竖线 “|” 把它们连起来,比如:
// 这个数组可以存放数字,也可以存放字符串
const arr: (number | string)[] = [1, "绿叶网", 2, "阿莫"];
arr.push(3);
arr.push("TS");