TypeScript 数组

在 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");

上一篇: TypeScript 基础类型

下一篇: TypeScript 元组

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号