数组与元组类型
TypeScript 提供了多种方式描述结构化数据,其中数组和元组是最基础的两种复合类型。它们都能存储有序元素集合,但在使用场景和类型约束上存在显著差异。
数组类型基础
数组类型表示由相同类型元素组成的可变长度序列。TypeScript 提供了两种等效的语法声明数组类型:
// 元素类型后接方括号
let numbers: number[] = [1, 2, 3];
// 使用泛型数组类型
let strings: Array<string> = ["a", "b"];
数组类型会强制所有元素保持类型一致。当尝试混合类型时,TypeScript 会抛出错误:
// 类型错误:不能将字符串分配给数字数组
numbers.push("4"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
数组类型推断
TypeScript 能根据初始化值自动推断数组类型。空数组需要显式注解,否则会被推断为 any[]
:
let inferredArray = [1, 2, 3]; // 自动推断为 number[]
let emptyArray = []; // 类型为 any[]
let typedEmptyArray: string[] = []; // 显式注解
只读数组
使用 ReadonlyArray
或 readonly
修饰符可以创建不可变数组:
const roArray: ReadonlyArray<number> = [1, 2, 3];
roArray.push(4); // Error: Property 'push' does not exist on type 'ReadonlyArray<number>'
// 使用更简洁的语法
const roNumbers: readonly number[] = [1, 2, 3];
元组类型基础
元组类型允许表示已知元素数量和类型的数组,各元素的类型不必相同:
let person: [string, number] = ["Alice", 30];
元组严格校验元素位置和类型:
person = [30, "Alice"]; // Error: Type 'number' is not assignable to type 'string'
person[2] = true; // Error: 长度为 "2" 的元组类型 "[string, number]" 在索引 "2" 处没有元素
可选元组元素
元组可以通过问号标记可选元素:
type OptionalTuple = [string, number?];
const a: OptionalTuple = ["hello"]; // 合法
const b: OptionalTuple = ["world", 42]; // 合法
剩余元素与开放元组
元组可以使用剩余语法表示可变长度部分:
type StringNumberBooleans = [string, number, ...boolean[]];
const snb: StringNumberBooleans = ["hello", 1, true, false, true];
元组与数组的区别
- 长度处理:
// 数组长度可变
const arr: number[] = [1];
arr.push(2); // 合法
// 元组长度固定
const tuple: [number] = [1];
tuple.push(2); // 类型检查通过但违反元组设计初衷
- 解构行为:
const rgb: [number, number, number] = [255, 0, 128];
const [red, green, blue] = rgb; // 精确解构
const color = [255, 0, 128]; // number[] 类型
const [r, g, b] = color; // 解构后类型均为 number
实际应用场景
数组典型用例:
// 处理同质数据集合
function sum(values: number[]): number {
return values.reduce((a, b) => a + b, 0);
}
元组典型用例:
// React useState 返回类型
type StateHook<T> = [T, (newValue: T) => void];
function useState<T>(initial: T): StateHook<T> {
// 实现省略
}
// CSV 行处理
function parseCSVLine(line: string): [string, number, Date] {
// 解析逻辑
return ["Alice", 30, new Date()];
}
类型操作与高级特性
联合类型数组:
let mixedArray: (string | number)[] = ["text", 42];
常量断言:
const point = [10, 20] as const; // 类型为 readonly [10, 20]
映射类型转换:
type TupleToObject<T extends readonly any[]> = {
[K in keyof T]: { value: T[K] }
};
type ObjTuple = TupleToObject<[string, number]>;
// 结果为 [{ value: string }, { value: number }]
常见问题与解决方案
元组长度溢出:
function processTuple(t: [string, number]) {
// ...
}
const input = ["test", 1, "extra"] as const;
processTuple(input); // Error: 源具有 3 个元素,但目标仅允许 2 个
// 解决方案1:类型断言
processTuple(input as [string, number]);
// 解决方案2:精确类型定义
function processDynamicTuple(t: readonly [string, number, ...unknown[]]) {
const [first, second] = t;
// ...
}
变异数组成员:
const tuple: [string, number] = ["a", 1];
tuple[0] = "b"; // 合法但可能不符合设计预期
// 防御性方案
type ImmutableTuple = readonly [string, number];
const immutable: ImmutableTuple = ["a", 1];
immutable[0] = "b"; // Error: 无法分配到 "0",因为它是只读属性
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:原始类型与字面量类型
下一篇:对象类型与接口