数组是编程中最常用的数据结构之一。TypeScript 为数组提供了丰富的类型支持,确保数组元素的类型一致性。元组则是 TypeScript 特有的类型,用于表示固定长度和类型的数组。
数组类型#
基本语法#
TypeScript 中定义数组有两种等价的语法:
// TypeScript 5.x
// 语法一:类型[](推荐)const numbers: number[] = [1, 2, 3, 4, 5]const names: string[] = ['张三', '李四', '王五']const flags: boolean[] = [true, false, true]
// 语法二:Array<类型>(泛型语法)const scores: Array<number> = [95, 88, 92]const users: Array<string> = ['user1', 'user2']🤔 两种语法效果完全相同,type[] 更简洁,Array<type> 在复杂类型时可读性更好。
类型推断#
初始化时 TypeScript 会自动推断数组类型:
// 自动推断为 number[]const nums = [1, 2, 3]
// 自动推断为 string[]const strs = ['a', 'b', 'c']
// 自动推断为 (string | number)[]const mixed = [1, 'hello', 2, 'world']空数组需要显式标注#
// ❌ 推断为 never[],无法添加元素const items = []// items.push('hello'); // 错误
// ✅ 显式标注类型const items2: string[] = []items2.push('hello') // 正确
// ✅ 或者初始化时提供元素const items3 = ['initial']items3.push('hello') // 正确多维数组#
// 二维数组const matrix: number[][] = [ [1, 2, 3], [4, 5, 6], [7, 8, 9],]
// 三维数组const cube: number[][][] = [ [ [1, 2], [3, 4], ], [ [5, 6], [7, 8], ],]
// 使用泛型语法const grid: Array<Array<number>> = [ [1, 2], [3, 4],]联合类型数组#
数组元素可以是多种类型:
// 数字或字符串数组const ids: (number | string)[] = [1, '2', 3, 'four']
// 注意括号位置// (number | string)[] - 数组,元素是 number 或 string// number | string[] - number 或 string数组(这可能不是你想要的)
// 包含 null 的数组const nullableItems: (string | null)[] = ['hello', null, 'world']
// 对象数组interface User { id: number name: string}const users: User[] = [ { id: 1, name: '张三' }, { id: 2, name: '李四' },]只读数组#
readonly 修饰符#
// 只读数组,不能修改const readonlyNums: readonly number[] = [1, 2, 3]
// ❌ 以下操作都会报错// readonlyNums.push(4);// readonlyNums.pop();// readonlyNums[0] = 10;
// ✅ 可以读取console.log(readonlyNums[0]) // 1console.log(readonlyNums.length) // 3ReadonlyArray 泛型#
const readonlyItems: ReadonlyArray<string> = ['a', 'b', 'c']
// 等价于const readonlyItems2: readonly string[] = ['a', 'b', 'c']🎯 只读数组的使用场景:
// 场景1:函数参数,防止意外修改function processItems(items: readonly string[]) { // items.push('new'); // ❌ 错误 return items.map((item) => item.toUpperCase()) // ✅ 返回新数组}
// 场景2:配置常量const ALLOWED_METHODS: readonly string[] = ['GET', 'POST', 'PUT', 'DELETE']
// 场景3:不可变数据模式function addItem(items: readonly number[], newItem: number): readonly number[] { return [...items, newItem] // 返回新数组}元组(Tuple)#
元组是固定长度和类型的数组,每个位置的类型可以不同。
基本用法#
// 定义元组类型let point: [number, number] = [10, 20]let person: [string, number] = ['张三', 25]let record: [number, string, boolean] = [1, 'active', true]
// 访问元素console.log(point[0]) // 10console.log(person[1]) // 25
// 解构赋值const [x, y] = pointconst [name, age] = person元组 vs 数组#
// 数组:长度可变,所有元素同类型const numbers: number[] = [1, 2, 3]numbers.push(4) // ✅ 可以添加
// 元组:固定长度,每个位置类型确定const pair: [string, number] = ['hello', 42]// pair.push('extra'); // TypeScript 允许,但逻辑上不推荐// pair[2]; // ❌ 错误:长度为 2 的元组没有索引 2🔶 元组的一个坑:TypeScript 允许对元组使用 push 等方法,但这会破坏元组的固定长度语义。
const tuple: [string, number] = ['hello', 1]tuple.push('extra') // 不报错!console.log(tuple) // ['hello', 1, 'extra']// 但 tuple[2] 仍然会报错可选元素#
// 第三个元素可选type Point2DOrPoint3D = [number, number, number?]
const point2D: Point2DOrPoint3D = [1, 2]const point3D: Point2DOrPoint3D = [1, 2, 3]
// 可选元素必须在末尾type HttpResponse = [number, string, object?]const success: HttpResponse = [200, 'OK', { data: [] }]const notFound: HttpResponse = [404, 'Not Found']剩余元素#
// 前两个是固定类型,后面可以有任意多个字符串type StringList = [number, number, ...string[]]
const list1: StringList = [1, 2]const list2: StringList = [1, 2, 'a']const list3: StringList = [1, 2, 'a', 'b', 'c']
// 实际应用:函数参数type Args = [string, ...number[]]function sum(label: string, ...nums: number[]): void { console.log( label, nums.reduce((a, b) => a + b, 0) )}sum('Total:', 1, 2, 3, 4, 5)只读元组#
// 只读元组const point: readonly [number, number] = [10, 20]// point[0] = 30; // ❌ 错误
// 使用 as const 创建只读元组const colors = ['red', 'green', 'blue'] as const// 类型是 readonly ["red", "green", "blue"]// colors.push('yellow'); // ❌ 错误// colors[0] = 'pink'; // ❌ 错误命名元组(TypeScript 4.0+)#
// 给元组元素命名,提高可读性type Person = [name: string, age: number, active: boolean]
const user: Person = ['张三', 25, true]
// 命名主要用于文档和提示,不影响实际使用function createPerson(...args: Person): void { const [name, age, active] = args console.log(name, age, active)}实际应用场景#
场景一:函数返回多个值#
// 使用元组返回多个值function useState<T>(initial: T): [T, (value: T) => void] { let state = initial const setState = (value: T) => { state = value } return [state, setState]}
// 使用时解构const [count, setCount] = useState(0)const [name, setName] = useState('张三')场景二:坐标和范围#
type Point = [x: number, y: number]type Range = [start: number, end: number]type RGB = [red: number, green: number, blue: number]
const origin: Point = [0, 0]const range: Range = [1, 100]const color: RGB = [255, 128, 0]
function distance(p1: Point, p2: Point): number { const [x1, y1] = p1 const [x2, y2] = p2 return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)}场景三:键值对#
type Entry<K, V> = [key: K, value: V]
const entries: Entry<string, number>[] = [ ['one', 1], ['two', 2], ['three', 3],]
// 配合 Object.entries 使用const obj = { a: 1, b: 2, c: 3 }const pairs: [string, number][] = Object.entries(obj)数组常用操作的类型#
const numbers: number[] = [1, 2, 3, 4, 5]
// map:转换类型const strings: string[] = numbers.map((n) => n.toString())
// filter:保持类型const evens: number[] = numbers.filter((n) => n % 2 === 0)
// reduce:指定返回类型const sum: number = numbers.reduce((acc, n) => acc + n, 0)
// find:返回 T | undefinedconst found: number | undefined = numbers.find((n) => n > 3)
// 类型守卫过滤const mixed: (string | null)[] = ['a', null, 'b', null, 'c']const filtered: string[] = mixed.filter((x): x is string => x !== null)常见问题#
🙋 元组和数组有什么本质区别?#
- 数组:同类型元素的集合,长度可变
- 元组:固定长度,每个位置有确定类型,用于表示结构化数据
// 数组适合:同类型的列表const scores: number[] = [90, 85, 88]
// 元组适合:结构化的固定数据const user: [number, string, boolean] = [1, '张三', true]🙋 为什么空数组推断为 never[]?#
因为 TypeScript 无法从空数组推断元素类型:
const arr = [] // never[]
// 解决方案const arr1: string[] = []const arr2 = [] as string[]const arr3: Array<string> = []🙋 as const 有什么作用?#
as const 会将数组转换为只读的字面量类型元组:
// 普通数组const arr1 = [1, 2, 3] // number[]
// as constconst arr2 = [1, 2, 3] as const // readonly [1, 2, 3]
// 常用于定义常量const DIRECTIONS = ['up', 'down', 'left', 'right'] as consttype Direction = (typeof DIRECTIONS)[number] // "up" | "down" | "left" | "right"总结#
| 类型 | 语法 | 特点 |
|---|---|---|
| 数组 | T[] 或 Array<T> | 可变长度,同类型元素 |
| 只读数组 | readonly T[] | 不可修改 |
| 元组 | [T1, T2, ...] | 固定长度,每位置类型确定 |
| 只读元组 | readonly [T1, T2] | 不可修改的元组 |
下一篇我们将学习对象类型,了解如何描述复杂的对象结构。