Appearance
TypeScript Array 数组
在 TypeScript 中,数组是一种特殊的数据类型,用于存储多个相同类型的元素。数组是 TypeScript 中最常用的数据结构之一,它提供了丰富的方法来操作和处理数据。本文将详细介绍 TypeScript 中的数组类型。
1. 基本用法
声明数组
TypeScript 中有两种方式来声明数组:
方式一:使用类型注解 + 方括号
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: string[] = ["apple", "banana", "cherry"];
let booleans: boolean[] = [true, false, true];方式二:使用泛型数组类型
typescript
let numbers: Array<number> = [1, 2, 3, 4, 5];
let strings: Array<string> = ["apple", "banana", "cherry"];
let booleans: Array<boolean> = [true, false, true];初始化数组
typescript
// 空数组
let emptyArray: number[] = [];
// 初始化数组
let numbers: number[] = [1, 2, 3, 4, 5];
// 使用 Array 构造函数
let numbers2: number[] = new Array(5); // 创建一个长度为 5 的空数组
let numbers3: number[] = new Array(1, 2, 3, 4, 5); // 创建包含元素的数组
// 使用 Array.from()
let numbers4: number[] = Array.from({ length: 5 }, (_, i) => i + 1); // [1, 2, 3, 4, 5]
// 使用 Array.of()
let numbers5: number[] = Array.of(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]访问数组元素
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
console.log(numbers[0]); // 输出:1
console.log(numbers[1]); // 输出:2
console.log(numbers[numbers.length - 1]); // 输出:5
// 修改元素
numbers[0] = 10;
console.log(numbers[0]); // 输出:10数组长度
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
console.log(numbers.length); // 输出:5
// 修改长度
numbers.length = 3;
console.log(numbers); // 输出:[1, 2, 3]
numbers.length = 5;
console.log(numbers); // 输出:[1, 2, 3, undefined, undefined]2. 数组类型注解
基本类型数组
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: string[] = ["apple", "banana", "cherry"];
let booleans: boolean[] = [true, false, true];
let nulls: null[] = [null, null, null];
let undefineds: undefined[] = [undefined, undefined, undefined];对象类型数组
typescript
interface Person {
name: string;
age: number;
}
let people: Person[] = [
{ name: "John", age: 30 },
{ name: "Jane", age: 25 },
{ name: "Bob", age: 35 }
];联合类型数组
typescript
let mixed: (string | number)[] = ["apple", 1, "banana", 2, "cherry", 3];嵌套数组
typescript
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
let nested: Array<Array<number>> = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];元组类型(固定长度和类型的数组)
typescript
let tuple: [string, number, boolean] = ["apple", 10, true];3. 数组方法
3.1 增删改查方法
push()
向数组末尾添加一个或多个元素,并返回新的长度。
typescript
let numbers: number[] = [1, 2, 3];
let length: number = numbers.push(4, 5);
console.log(numbers); // 输出:[1, 2, 3, 4, 5]
console.log(length); // 输出:5pop()
从数组末尾移除一个元素,并返回该元素。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let last: number = numbers.pop();
console.log(numbers); // 输出:[1, 2, 3, 4]
console.log(last); // 输出:5unshift()
向数组开头添加一个或多个元素,并返回新的长度。
typescript
let numbers: number[] = [3, 4, 5];
let length: number = numbers.unshift(1, 2);
console.log(numbers); // 输出:[1, 2, 3, 4, 5]
console.log(length); // 输出:5shift()
从数组开头移除一个元素,并返回该元素。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let first: number = numbers.shift();
console.log(numbers); // 输出:[2, 3, 4, 5]
console.log(first); // 输出:1splice()
添加、删除或替换数组中的元素。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
// 删除元素
let removed: number[] = numbers.splice(1, 2); // 从索引 1 开始删除 2 个元素
console.log(numbers); // 输出:[1, 4, 5]
console.log(removed); // 输出:[2, 3]
// 添加元素
numbers.splice(1, 0, 2, 3); // 从索引 1 开始,删除 0 个元素,添加 2 和 3
console.log(numbers); // 输出:[1, 2, 3, 4, 5]
// 替换元素
numbers.splice(1, 2, 6, 7); // 从索引 1 开始,删除 2 个元素,添加 6 和 7
console.log(numbers); // 输出:[1, 6, 7, 4, 5]slice()
提取数组的一部分,返回新数组。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let slice1: number[] = numbers.slice(1, 4); // 从索引 1 开始,到索引 4 结束(不包括 4)
console.log(slice1); // 输出:[2, 3, 4]
let slice2: number[] = numbers.slice(2); // 从索引 2 开始到结束
console.log(slice2); // 输出:[3, 4, 5]
let slice3: number[] = numbers.slice(-3); // 从倒数第 3 个元素开始到结束
console.log(slice3); // 输出:[3, 4, 5]3.2 遍历方法
forEach()
遍历数组中的每个元素,执行回调函数。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
numbers.forEach((value, index, array) => {
console.log(`Index ${index}: ${value}`);
});
// 输出:
// Index 0: 1
// Index 1: 2
// Index 2: 3
// Index 3: 4
// Index 4: 5map()
遍历数组中的每个元素,执行回调函数,返回新数组。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let doubled: number[] = numbers.map((value) => value * 2);
console.log(doubled); // 输出:[2, 4, 6, 8, 10]
let strings: string[] = numbers.map((value) => `Number: ${value}`);
console.log(strings); // 输出:["Number: 1", "Number: 2", "Number: 3", "Number: 4", "Number: 5"]filter()
遍历数组中的每个元素,执行回调函数,返回符合条件的元素组成的新数组。
typescript
let numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let even: number[] = numbers.filter((value) => value % 2 === 0);
console.log(even); // 输出:[2, 4, 6, 8, 10]
let greaterThan5: number[] = numbers.filter((value) => value > 5);
console.log(greaterThan5); // 输出:[6, 7, 8, 9, 10]reduce()
遍历数组中的每个元素,执行回调函数,将结果累积到一个值中。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
// 求和
let sum: number = numbers.reduce((accumulator, value) => accumulator + value, 0);
console.log(sum); // 输出:15
// 求积
let product: number = numbers.reduce((accumulator, value) => accumulator * value, 1);
console.log(product); // 输出:120
// 找出最大值
let max: number = numbers.reduce((accumulator, value) => Math.max(accumulator, value), -Infinity);
console.log(max); // 输出:5reduceRight()
与 reduce() 类似,但从数组末尾开始遍历。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let reversed: number[] = numbers.reduceRight((accumulator, value) => {
accumulator.push(value);
return accumulator;
}, [] as number[]);
console.log(reversed); // 输出:[5, 4, 3, 2, 1]every()
检查数组中的所有元素是否都符合条件,返回布尔值。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let allPositive: boolean = numbers.every((value) => value > 0);
console.log(allPositive); // 输出:true
let allEven: boolean = numbers.every((value) => value % 2 === 0);
console.log(allEven); // 输出:falsesome()
检查数组中是否有至少一个元素符合条件,返回布尔值。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let hasEven: boolean = numbers.some((value) => value % 2 === 0);
console.log(hasEven); // 输出:true
let hasNegative: boolean = numbers.some((value) => value < 0);
console.log(hasNegative); // 输出:falsefind()
返回数组中第一个符合条件的元素,如果没有则返回 undefined。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let firstEven: number | undefined = numbers.find((value) => value % 2 === 0);
console.log(firstEven); // 输出:2
let firstNegative: number | undefined = numbers.find((value) => value < 0);
console.log(firstNegative); // 输出:undefinedfindIndex()
返回数组中第一个符合条件的元素的索引,如果没有则返回 -1。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let firstEvenIndex: number = numbers.findIndex((value) => value % 2 === 0);
console.log(firstEvenIndex); // 输出:1
let firstNegativeIndex: number = numbers.findIndex((value) => value < 0);
console.log(firstNegativeIndex); // 输出:-1findLast()
返回数组中最后一个符合条件的元素,如果没有则返回 undefined。
typescript
let numbers: number[] = [1, 2, 3, 4, 5, 6];
let lastEven: number | undefined = numbers.findLast((value) => value % 2 === 0);
console.log(lastEven); // 输出:6findLastIndex()
返回数组中最后一个符合条件的元素的索引,如果没有则返回 -1。
typescript
let numbers: number[] = [1, 2, 3, 4, 5, 6];
let lastEvenIndex: number = numbers.findLastIndex((value) => value % 2 === 0);
console.log(lastEvenIndex); // 输出:53.3 其他方法
concat()
连接两个或多个数组,返回新数组。
typescript
let arr1: number[] = [1, 2, 3];
let arr2: number[] = [4, 5, 6];
let arr3: number[] = [7, 8, 9];
let combined: number[] = arr1.concat(arr2, arr3);
console.log(combined); // 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9]join()
将数组中的元素连接成一个字符串,返回该字符串。
typescript
let fruits: string[] = ["apple", "banana", "cherry"];
let joined: string = fruits.join(", ");
console.log(joined); // 输出:"apple, banana, cherry"
let joinedWithDash: string = fruits.join("-");
console.log(joinedWithDash); // 输出:"apple-banana-cherry"reverse()
反转数组中的元素,修改原数组。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
numbers.reverse();
console.log(numbers); // 输出:[5, 4, 3, 2, 1]sort()
排序数组中的元素,修改原数组。
typescript
let numbers: number[] = [3, 1, 4, 1, 5, 9, 2, 6];
// 默认排序(按字符串顺序)
numbers.sort();
console.log(numbers); // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
// 自定义排序
numbers.sort((a, b) => b - a); // 降序
console.log(numbers); // 输出:[9, 6, 5, 4, 3, 2, 1, 1]includes()
检查数组是否包含指定元素,返回布尔值。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
let has3: boolean = numbers.includes(3);
console.log(has3); // 输出:true
let has6: boolean = numbers.includes(6);
console.log(has6); // 输出:false
// 从指定索引开始搜索
let has3FromIndex2: boolean = numbers.includes(3, 2);
console.log(has3FromIndex2); // 输出:true
let has3FromIndex4: boolean = numbers.includes(3, 4);
console.log(has3FromIndex4); // 输出:falseindexOf()
返回指定元素在数组中第一次出现的索引,如果没有则返回 -1。
typescript
let numbers: number[] = [1, 2, 3, 4, 3, 5];
let index: number = numbers.indexOf(3);
console.log(index); // 输出:2
let notFound: number = numbers.indexOf(6);
console.log(notFound); // 输出:-1
// 从指定索引开始搜索
let indexFrom2: number = numbers.indexOf(3, 3);
console.log(indexFrom2); // 输出:4lastIndexOf()
返回指定元素在数组中最后一次出现的索引,如果没有则返回 -1。
typescript
let numbers: number[] = [1, 2, 3, 4, 3, 5];
let lastIndex: number = numbers.lastIndexOf(3);
console.log(lastIndex); // 输出:4
let notFound: number = numbers.lastIndexOf(6);
console.log(notFound); // 输出:-1
// 从指定索引开始搜索(从后向前)
let lastIndexFrom3: number = numbers.lastIndexOf(3, 3);
console.log(lastIndexFrom3); // 输出:2fill()
用指定值填充数组中的元素,修改原数组。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
// 填充所有元素
numbers.fill(0);
console.log(numbers); // 输出:[0, 0, 0, 0, 0]
// 从指定索引开始填充
numbers.fill(1, 1);
console.log(numbers); // 输出:[0, 1, 1, 1, 1]
// 从指定索引开始,到指定索引结束填充
numbers.fill(2, 2, 4);
console.log(numbers); // 输出:[0, 1, 2, 2, 1]flat()
将嵌套数组扁平化为指定深度的数组,返回新数组。
typescript
let nested: number[][] = [[1, 2], [3, 4], [5, 6]];
let flattened: number[] = nested.flat();
console.log(flattened); // 输出:[1, 2, 3, 4, 5, 6]
let deeplyNested: number[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]];
let flattened2: number[] = deeplyNested.flat(2);
console.log(flattened2); // 输出:[1, 2, 3, 4, 5, 6, 7, 8]flatMap()
先对数组中的每个元素执行 map(),然后对结果执行 flat(),返回新数组。
typescript
let numbers: number[] = [1, 2, 3];
let result: number[] = numbers.flatMap((value) => [value, value * 2]);
console.log(result); // 输出:[1, 2, 2, 4, 3, 6]copyWithin()
将数组中的一部分元素复制到另一部分,修改原数组。
typescript
let numbers: number[] = [1, 2, 3, 4, 5];
// 从索引 0 开始,将索引 3 及以后的元素复制到索引 0 处
numbers.copyWithin(0, 3);
console.log(numbers); // 输出:[4, 5, 3, 4, 5]
// 从索引 1 开始,将索引 0 到 2 的元素复制到索引 1 处
numbers.copyWithin(1, 0, 2);
console.log(numbers); // 输出:[4, 4, 5, 4, 5]entries()
返回一个包含数组所有键值对的迭代器。
typescript
let fruits: string[] = ["apple", "banana", "cherry"];
for (const [index, value] of fruits.entries()) {
console.log(`${index}: ${value}`);
}
// 输出:
// 0: apple
// 1: banana
// 2: cherrykeys()
返回一个包含数组所有索引的迭代器。
typescript
let fruits: string[] = ["apple", "banana", "cherry"];
for (const index of fruits.keys()) {
console.log(index);
}
// 输出:
// 0
// 1
// 2values()
返回一个包含数组所有值的迭代器。
typescript
let fruits: string[] = ["apple", "banana", "cherry"];
for (const value of fruits.values()) {
console.log(value);
}
// 输出:
// apple
// banana
// cherry4. 类型守卫
在 TypeScript 中,可以使用类型守卫来检查一个值是否是数组类型。
Array.isArray() 类型守卫
typescript
function processValue(value: any) {
if (Array.isArray(value)) {
// 在这个分支中,TypeScript 知道 value 是数组类型
console.log(`Array length: ${value.length}`);
value.forEach((item: any) => console.log(item));
} else {
console.log(`Not an array: ${value}`);
}
}
processValue([1, 2, 3]); // 输出:Array length: 3 1 2 3
processValue("hello"); // 输出:Not an array: hello
processValue(123); // 输出:Not an array: 123自定义类型守卫
typescript
function isNumberArray(value: any): value is number[] {
return Array.isArray(value) && value.every((item) => typeof item === "number");
}
function processValue(value: any) {
if (isNumberArray(value)) {
// 在这个分支中,TypeScript 知道 value 是 number[] 类型
let sum = value.reduce((acc: number, num: number) => acc + num, 0);
console.log(`Sum: ${sum}`);
} else {
console.log(`Not a number array: ${value}`);
}
}
processValue([1, 2, 3]); // 输出:Sum: 6
processValue(["1", "2", "3"]); // 输出:Not a number array: 1,2,3
processValue("hello"); // 输出:Not a number array: hello5. 数组操作的最佳实践
1. 使用类型注解
为数组添加类型注解可以提高代码的可读性和类型安全性。
typescript
// 推荐
let numbers: number[] = [1, 2, 3, 4, 5];
// 不推荐
let numbers = [1, 2, 3, 4, 5]; // 类型推断为 number[],但不如显式注解清晰2. 使用 const 声明只读数组
对于不需要修改的数组,使用 const 声明可以防止意外修改。
typescript
// 推荐
const numbers: number[] = [1, 2, 3, 4, 5];
// 不推荐
let numbers: number[] = [1, 2, 3, 4, 5]; // 可能被意外修改3. 使用函数式方法
对于数组操作,优先使用函数式方法(如 map、filter、reduce 等),而不是命令式方法(如 for 循环)。
typescript
// 推荐
let numbers: number[] = [1, 2, 3, 4, 5];
let doubled: number[] = numbers.map((num) => num * 2);
// 不推荐
let numbers: number[] = [1, 2, 3, 4, 5];
let doubled: number[] = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}4. 注意数组方法的返回值
许多数组方法返回新数组,而不是修改原数组。
typescript
// 推荐
let numbers: number[] = [1, 2, 3, 4, 5];
let doubled: number[] = numbers.map((num) => num * 2);
// 不推荐
let numbers: number[] = [1, 2, 3, 4, 5];
numbers.map((num) => num * 2); // 原数组不变,返回值被忽略5. 避免使用 delete 操作符
使用 delete 操作符删除数组元素会留下 undefined 占位符,推荐使用 splice() 方法。
typescript
// 推荐
let numbers: number[] = [1, 2, 3, 4, 5];
numbers.splice(2, 1); // 删除索引 2 处的元素
console.log(numbers); // 输出:[1, 2, 4, 5]
// 不推荐
let numbers: number[] = [1, 2, 3, 4, 5];
delete numbers[2]; // 删除索引 2 处的元素
console.log(numbers); // 输出:[1, 2, undefined, 4, 5]6. 合理使用数组方法的参数
了解数组方法的参数含义,避免使用错误的参数。
typescript
// 推荐:正确使用 slice() 方法
let numbers: number[] = [1, 2, 3, 4, 5];
let slice: number[] = numbers.slice(1, 4); // 从索引 1 开始,到索引 4 结束(不包括 4)
console.log(slice); // 输出:[2, 3, 4]
// 不推荐:错误使用 slice() 方法
let numbers: number[] = [1, 2, 3, 4, 5];
let slice: number[] = numbers.slice(1, 3); // 期望得到 [2, 3, 4],但实际得到 [2, 3]7. 注意数组的长度
修改数组的长度可能会导致意外的行为,特别是当长度小于原长度时。
typescript
// 注意:修改数组长度可能会导致数据丢失
let numbers: number[] = [1, 2, 3, 4, 5];
numbers.length = 3; // 截断数组
console.log(numbers); // 输出:[1, 2, 3]
// 注意:增加数组长度会添加 undefined 元素
numbers.length = 5;
console.log(numbers); // 输出:[1, 2, 3, undefined, undefined]6. 常见错误
1. 类型不匹配
在 TypeScript 中,数组元素的类型必须与数组的类型注解匹配。
typescript
// 错误:类型不匹配
let numbers: number[] = [1, 2, "3", 4, 5]; // 类型 'string' 不能赋值给类型 'number'
// 正确
let numbers: number[] = [1, 2, 3, 4, 5];
let mixed: (number | string)[] = [1, 2, "3", 4, 5];2. 访问越界索引
访问数组中不存在的索引会返回 undefined。
typescript
let numbers: number[] = [1, 2, 3];
console.log(numbers[5]); // 输出:undefined3. 忘记使用返回值
许多数组方法返回新数组,而不是修改原数组。
typescript
// 错误:忘记使用返回值
let numbers: number[] = [1, 2, 3, 4, 5];
numbers.map((num) => num * 2); // 原数组不变
console.log(numbers); // 输出:[1, 2, 3, 4, 5]
// 正确:使用返回值
let numbers: number[] = [1, 2, 3, 4, 5];
let doubled: number[] = numbers.map((num) => num * 2);
console.log(doubled); // 输出:[2, 4, 6, 8, 10]4. 混淆修改原数组和返回新数组的方法
一些数组方法会修改原数组,而另一些会返回新数组。
typescript
// 修改原数组的方法:push, pop, unshift, shift, splice, reverse, sort, fill, copyWithin
let numbers: number[] = [1, 2, 3, 4, 5];
numbers.push(6);
console.log(numbers); // 输出:[1, 2, 3, 4, 5, 6]
// 返回新数组的方法:slice, concat, map, filter, reduce, flat, flatMap
let numbers: number[] = [1, 2, 3, 4, 5];
let sliced: number[] = numbers.slice(1, 4);
console.log(numbers); // 输出:[1, 2, 3, 4, 5](原数组不变)
console.log(sliced); // 输出:[2, 3, 4](新数组)5. 错误使用 sort() 方法
sort() 方法默认按字符串顺序排序,对于数字排序需要提供比较函数。
typescript
// 错误:默认按字符串排序
let numbers: number[] = [10, 2, 5, 1, 8];
numbers.sort();
console.log(numbers); // 输出:[1, 10, 2, 5, 8](错误的排序)
// 正确:提供比较函数
let numbers: number[] = [10, 2, 5, 1, 8];
numbers.sort((a, b) => a - b); // 升序
console.log(numbers); // 输出:[1, 2, 5, 8, 10](正确的排序)6. 错误使用 reduce() 方法
reduce() 方法需要提供初始值,特别是当数组可能为空时。
typescript
// 错误:没有提供初始值,当数组为空时会报错
let numbers: number[] = [];
let sum: number = numbers.reduce((acc, num) => acc + num); // 运行时错误:Reduce of empty array with no initial value
// 正确:提供初始值
let numbers: number[] = [];
let sum: number = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 输出:0总结
TypeScript 中的数组是一种强大的数据结构,用于存储多个相同类型的元素。它包括:
- 基本用法:声明和初始化数组,访问数组元素,修改数组长度
- 类型注解:基本类型数组,对象类型数组,联合类型数组,嵌套数组,元组类型
- 数组方法:增删改查方法,遍历方法,其他方法
- 类型守卫:使用 Array.isArray() 和自定义类型守卫检查数组类型
- 最佳实践:使用类型注解,使用 const 声明只读数组,使用函数式方法,注意数组方法的返回值,避免使用 delete 操作符,合理使用数组方法的参数,注意数组的长度
- 常见错误:类型不匹配,访问越界索引,忘记使用返回值,混淆修改原数组和返回新数组的方法,错误使用 sort() 方法,错误使用 reduce() 方法
通过合理使用数组和相关方法,你可以在 TypeScript 中高效地处理和操作数据,编写更清晰、更安全的代码。