TypeScript 学习笔记(四):数组和元组

前言

在上一篇文章中,我们学习了TS的三种特殊类型。这篇文章我们主要学习TS的数组和元组。

数组

TS数组有一个根本特征:所有成员的类型必须相同,但是成员数量是不确定的,可以是无限数量的成员,也可以是零成员。

写法

数组的类型有两种写法。

  • 第一种写法是在数组成员的类型后面,加上一对方括号。
TS 复制代码
let arr:number[] = [1, 2, 3];

上面示例中,数组arr的类型是number[],其中number表示数组成员类型是number

  • 数组类型的第二种写法是使用 TypeScript 内置的 Array 接口。
TS 复制代码
let arr:Array<number> = [1, 2, 3];

上面示例中,数组arr的类型是Array<number>,其中number表示成员类型是number

使用

数组类型声明了以后,成员数量是不限制的,任意数量的成员都可以,也可以是空数组。

TS 复制代码
let arr:number[];
arr = [];
arr = [1];
arr = [1, 2];
arr = [1, 2, 3];

数组增加成员或减少成员,都是可以的。

TS 复制代码
let arr:number[] = [1, 2, 3];

arr[3] = 4;
arr.length = 2;

由于成员数量可以动态变化,所以TS不会对数组边界进行检查,越界访问数组并不会报错。

TS 复制代码
let arr:number[] = [1, 2, 3];
let Number = arr[3]; // 正确

上面示例中,变量Number的值是一个不存在的数组成员,但是TS并不会报错。

数组的类型推断

如果数组变量没有声明类型,TS就会推断数组成员的类型。这时,推断行为会因为值的不同,而有所不同。

如果变量的初始值是空数组,那么TS会推断数组类型是any[]

TS 复制代码
// 推断为 any[]
const arr = [];

后面,为这个数组赋值时,TS会自动更新类型推断。

TS 复制代码
const arr = [];
arr // 推断为 any[]

arr.push(123);
arr // 推断类型为 number[]

arr.push('abc');
arr // 推断类型为 (string|number)[]

但是,类型推断的自动更新只发生初始值为空数组的情况。 如果初始值不是空数组,类型推断就不会更新。

TS 复制代码
// 推断类型为 number[]
const arr = [123];

arr.push('abc'); // 报错

上面示例中,数组变量arr的初始值是[123]TS就推断成员类型为number。新成员如果不是这个类型,TS就会报错,而不会更新类型推断。

只读数组

JS规定,const命令声明的数组变量是可以改变成员的。

JS 复制代码
const arr = [0, 1];
arr[0] = 2;

上面示例中,修改const命令声明的数组的成员是允许的。

但是,很多时候确实有声明为只读数组的需求,即不允许变动数组成员。TS允许声明只读数组,方法是在数组类型前面加上readonly关键字。

TS 复制代码
const arr:readonly number[] = [0, 1];

arr[1] = 2; // 报错
arr.push(3); // 报错
delete arr[0]; // 报错

TypeScript 将readonly number[]number[]视为两种不一样的类型,后者是前者的子类型。

我们知道,子类型继承了父类型的所有特征,并加上了自己的特征,所以子类型number[]可以用于所有使用父类型的场合,反过来就不行。

TS 复制代码
let a1:number[] = [0, 1];
let a2:readonly number[] = a1; // 正确

a1 = a2; // 报错

上面示例中,子类型number[]可以赋值给父类型readonly number[],但是反过来就会报错。

const断言

只读数组还有一种声明方法,就是使用"const 断言"。

TS 复制代码
const arr = [0, 1] as const;

arr[0] = [2]; // 报错 

上面示例中,as const告诉TS,推断类型时要把变量arr推断为只读数组,从而使得数组成员无法改变。

元组

元组(tuple)是TS特有的数据类型,JS没有单独区分这种类型。它表示成员类型可以自由设置的数组,即数组的各个成员的类型可以不同。

元组的使用说明

由于成员的类型可以不一样,所以元组必须明确声明每个成员的类型。

TS 复制代码
const s:[string, string, boolean]
  = ['a', 'b', true];

上面示例中,元组s的前两个成员的类型是string,最后一个成员的类型是boolean

数组的成员类型写在方括号外面(number[]),元组的成员类型是写在方括号里面([number])。TS的区分方法就是,成员类型写在方括号里面的就是元组,写在外面的就是数组。

TS 复制代码
// 数组
let a:number[] = [1];

// 元组
let t:[number] = [1];

使用元组时,必须明确给出类型声明(上例的[number]),不能省略,否则TS会把一个值自动推断为数组。

TS 复制代码
// a 的类型被推断为 (number | boolean)[]
let a = [1, true];

元组成员的类型可以添加问号后缀(?),表示该成员是可选的。 下面示例中,元组a的第二个成员是可选的,可以省略。

TS 复制代码
let a:[number, number?] = [1];

注意,问号只能用于元组的尾部成员,也就是说,所有可选成员必须在必选成员之后。

TS 复制代码
type myTuple = [
  number,
  number,
  number?,
  string?
];

由于需要声明每个成员的类型,所以大多数情况下,元组的成员数量是有限的,从类型声明就可以明确知道,元组包含多少个成员,越界的成员会报错。

TS 复制代码
let x:[string, string] = ['a', 'b'];

x[2] = 'c'; // 报错

只读元组

元组也可以是只读的,不允许修改,有两种写法。

TS 复制代码
// 写法一
type t = readonly [number, string]

// 写法二
type t = Readonly<[number, string]>

上面示例中,两种写法都可以得到只读元组,其中写法二是一个泛型,用到了工具类型Readonly<T>跟数组一样,只读元组是元组的父类型。所以,元组可以替代只读元组,而只读元组不能替代元组。

下面示例中,类型t1是只读元组,类型t2是普通元组。t2类型可以赋值给t1类型,反过来就会报错。

TS 复制代码
type t1 = readonly [number, number];
type t2 = [number, number];

let x:t2 = [1, 2];
let y:t1 = x; // 正确

x = y; // 报错
相关推荐
gqkmiss21 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃27 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰31 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye37 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm40 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You2 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生2 小时前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互