TypeScript 元组(Tuple)详解

222 阅读3分钟

TypeScript 元组(Tuple)详解

目录

  1. 概述
  2. 基本用法
  3. 高级特性
  4. 实际应用场景
  5. 最佳实践
  6. 注意事项

概述

元组(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];  // 错误

总结

  1. 元组的优点:

    • 固定长度
    • 类型安全
    • 支持不同类型
    • 可读���好(使用标签)
  2. 使用场景:

    • 返回多个值
    • 表示坐标
    • 固定格式数据
    • 状态管理
  3. 注意事项:

    • 长度固定
    • 类型严格
    • 可选元素规则
    • 考虑可读性