TypeScript 元组(Tuple)详解
目录
概述
元组(Tuple)是 TypeScript 中特有的类型,它允许我们定义一个固定长度和固定类型的数组,其中每个元素的类型都可以不同。
与数组的区别
// 数组:所有元素类型必须相同
let arr: string[] = ["Hello", "World"];
// 元组:每个位置的元素可以是不同的类型
let tuple: [string, number] = ["Hello", 42];
基本用法
1. 基本声明
// 基本的元组类型
let person: [string, number] = ["John", 30];
// 访问元素
console.log(person[0]); // "John"
console.log(person[1]); // 30
// 解构赋值
const [name, age] = person;
2. 可选元素
// 使用 ? 标记可选元素
let optionalTuple: [string, number?] = ["Hello"];
optionalTuple = ["Hello", 42]; // 也可以
// 多个可选元素
type OptionalTuple = [string, number?, boolean?];
let tuple1: OptionalTuple = ["Hello"];
let tuple2: OptionalTuple = ["Hello", 42];
let tuple3: OptionalTuple = ["Hello", 42, true];
3. 只读元组
// 使用 readonly 修饰符
const readonlyTuple: readonly [string, number] = ["Hello", 42];
// 无法修改元素
readonlyTuple[0] = "Hi"; // 错误:无法分配到 '0' ,因为它是只读属性。
高级特性
1. 剩余元素
// 使用展开运算符定义剩余元素
type StringNumberBooleans = [string, number, ...boolean[]];
let tuple: StringNumberBooleans = ["Hello", 42, true, false, true];
// 混合类型的剩余元素
type Mixed = [string, ...(string | number)[], boolean];
let mixedTuple: Mixed = ["start", "middle", 42, 100, "end", true];
2. 元组类型推断
// 从数组字面量推断
let tuple = ["Hello", 42] as const; // readonly ["Hello", 42]
// 从函数返回值推断
function getTuple() {
return ["Hello", 42] as const;
}
3. 元组标签
// 使用标签提高可读性
type UserInfo = [name: string, age: number, active: boolean];
let user: UserInfo = ["John", 30, true];
// 带可选标签的元组
type Config = [path: string, timeout?: number];
实际应用场景
1. 函数返回几个值
function getUserInfo(): [string, number, boolean] {
return ["John", 30, true];
}
const [name, age, isActive] = getUserInfo();
2. React useState
// useState 的简化实现
function useState<T>(initial: T): [T, (value: T) => void] {
let value = initial;
const setValue = (newValue: T) => {
value = newValue;
};
return [value, setValue];
}
// 使用
const [count, setCount] = useState(0);
3. 坐标系统
type Point2D = [x: number, y: number];
type Point3D = [x: number, y: number, z: number];
function calculateDistance(p1: Point2D, p2: Point2D): number {
const [x1, y1] = p1;
const [x2, y2] = p2;
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
4. 解析 CSV 数据
type CSVRow = [string, number, Date];
function parseCSVLine(line: string): CSVRow {
const [name, ageStr, dateStr] = line.split(',');
return [
name,
parseInt(ageStr),
new Date(dateStr)
];
}
最佳实践
1. 使用标签提高可读性
// 不好的写法
type UserData = [string, number, boolean];
// 好的写法
type UserData = [name: string, age: number, isActive: boolean];
2. 考虑使用接口替代复杂元组
// 元组写法
type ComplexTuple = [string, number, boolean, string[], object];
// 更好的接口写法
interface ComplexData {
name: string;
age: number;
isActive: boolean;
tags: string[];
metadata: object;
}
3. 使用 const 断言
// 不好的写法
let config = ["localhost", 8080] as [string, number];
// 好的写法
const config = ["localhost", 8080] as const;
注意事项
1. 长度限制
let tuple: [string, number] = ["Hello", 42];
tuple[2] = "World"; // 错误:索引超出范围
// 但可以使用数组方法
tuple.push("World"); // 这是允许的,但不推荐
2. 类型兼容性
// 数组不能赋值给元组
let arr: string[] = ["Hello", "World"];
let tuple: [string, string] = arr; // 错误
// 元组可以赋值给数组
let tuple: [string, string] = ["Hello", "World"];
let arr: string[] = tuple; // 正确
3. 可选元素的限制
type OptionalTuple = [string, number?, boolean?];
// 可选元素必须在必选元素之后
type Invalid = [string?, number, boolean]; // 错误
// 可选元素后面不能有必选元素
type Invalid2 = [string?, number?, boolean]; // 错误
总结
-
元组的优点:
- 固定长度
- 类型安全
- 支持不同类型
- 可读���好(使用标签)
-
使用场景:
- 返回多个值
- 表示坐标
- 固定格式数据
- 状态管理
-
注意事项:
- 长度固定
- 类型严格
- 可选元素规则
- 考虑可读性