- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
元组类型(Tuple Type)是 TypeScript 类型系统中表示"固定长度、固定顺序、逐元素定类型"的数组的关键工具。本文以《The Concise TypeScript Book》(本仓库对应的多语言开源电子书)中 pt-br/book/tuple-type-anonymous.md 为核心骨架,结合仓库中固定长度元组、命名元组、只读元组与可变元组等相邻章节,系统讲解匿名元组类型的定义、用法、边界行为与进阶模式。读完本文,你将能准确区分元组与普通数组,并熟练使用元组声明坐标、键值对、函数多返回值等真实场景中的类型约束。
什么是元组类型(Tuple Type)
按《The Concise TypeScript Book》的定义,元组类型(Tuple Type)是表示一个包含固定数量元素、且每个元素具有对应类型的数组的类型。它的核心约束有两点:
- 元素数量固定:声明了多少个位置,就必须提供多少个元素;
- 位置语义固定:每个位置的类型在声明时确定,不能随意替换顺序或类型。
与普通数组类型(如number[],只约束"元素都是 number")不同,元组类型把"第几个位置是什么类型"写进了类型系统,因此当你需要让数组中每个位置都有特定含义时,元组是最直接的表达方式。
原文档给出的最小示例:
type Point = [number, number];这一行声明了一个匿名元组类型Point:它恰好包含两个元素,且两个元素都必须是number。所谓"匿名(Anonymous)",是指元组的每个位置没有显式命名,只靠位置(索引)约定含义,这正是它与下文"命名元组(Named/Labeled Tuple)"的根本区别。
基本用法与核心语法
声明元组类型
元组类型的字面量语法就是用方括号包裹一组类型,元素类型之间用逗号分隔:
type Point = [number, number]; type NameAndAge = [string, number]; type Mixed = [string, number, boolean];每个位置的类型可以是任意 TypeScript 类型——基础类型、字面量类型、联合类型、接口,甚至另一个元组:
type Coordinate3D = [number, number, number]; type IdAndLabels = [number, ...string[]]; // 固定头 + 可变尾部 type Pair<T> = [T, T]; // 泛型元组给变量标注元组类型
元组类型最常见的消费方式是为变量或函数参数做类型标注:
const point: Point = [10, 20]; // 正确:两个 number const bad: Point = [10, "20"]; // 错误:位置 1 必须是 number const tooShort: Point = [10]; // 错误:缺少一个元素 const tooLong: Point = [10, 20, 30]; // 错误:元素数量超出解构赋值中的元组
在仓库的 pt-br/book/others.md("Tipos de Tupla Variádicos"一节)中,给出了元组配合数组解构的典型写法:
type Student = [string, number]; const [name, age]: Student = ['Simone', 20];由于元组每个位置类型已知,解构出的name会被精确推断为string、age为number,这比从(string | number)[]中解构再手动收窄要安全得多。
元组 vs 数组:何时该用哪个
普通数组类型(number[]或Array<number>)只约束元素类型,不约束长度;元组类型两者都约束。二者的取舍可以概括为:
| 维度 | 普通数组number[] | 元组[number, number] |
|---|---|---|
| 长度约束 | 任意长度 | 固定长度 |
| 位置语义 | 无(所有位置同类型) | 每个位置类型可不同且有含义 |
| 典型场景 | 同质集合(列表、集合) | 异质且定长的数据(坐标、记录) |
| 可变性 | 可用push/pop改变长度 | 长度被类型系统锁定 |
从源码佐证看,仓库在 pt-br/book/primitive-types.md 中并行展示了数组与元组的声明差异,并引入了只读版本:
const x: [string, number] = ['a', 1]; // 可变元组 const y: readonly [string, number] = ['a', 1]; // 只读元组也就是说,TypeScript 同时支持"可变元组"与"只读元组"两种形态,后者禁止对元组做任何会改变内容的操作。
现实应用场景
1. 坐标与几何数据
原文档示例中的Point就是最典型的应用——用固定顺序表示二维坐标:
type Point = [number, number]; const p: Point = [3, 5]; function distance(a: Point, b: Point): number { return Math.sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2); }这里[0]恒为 x 坐标、[1]恒为 y 坐标,位置本身携带了语义。
2. 键值对 / CSV 记录
当一条记录恰好包含固定数量、类型各异的字段时,元组比对象字面量更轻量:
type Entry = [string, number]; // 名称 + 数量 const entries: Entry[] = [ ['apple', 3], ['orange', 5], ];3. 函数的多返回值
函数返回元组,可以让调用方一次拿到多个不同类型的值,并用解构分别接收:
function splitName(fullName: string): [string, string] { const parts = fullName.split(' '); return [parts[0], parts[1]]; } const [firstName, lastName] = splitName('Simone Rossi');进阶一:固定长度元组与as const
仓库 pt-br/book/fixed-length-tuple.md 专门讲解了固定长度元组(Fixed Length Tuple):它强制固定数量的特定类型元素,并且一旦定义,就不允许再修改元组的长度。
文档给出的关键示例:
const x = [10, 'hello'] as const; x.push(2); // Erro(错误:属性 'push' 在只读元组上不存在)通过as const断言,x被推断为只读元组readonly [10, "hello"],字面量类型被保留(10而非number),且push这类改变长度的操作在编译期即被禁止。
在 pt-br/book/exploring-the-type-system.md 的"Const Assertion"一节中,对这种差异有更直观的对比:
const x = [1, 2, 3]; // 推断为 number[](可变数组) const y = [1, 2, 3] as const; // 推断为 readonly [1, 2, 3](只读元组)可以总结出三条实用结论:
- 显式声明元组类型(
const p: [number, number] = [1, 2])能锁定长度与类型; as const推断能保留字面量类型并附加只读性,适合配置常量、路由表等场景;- 一旦变成只读元组,
push、pop、splice等会修改数组结构的操作都会被类型系统拒绝。
进阶二:命名元组(Labeled Tuples)与匿名元组的混合
仓库 pt-br/book/named-tuple-type-labeled.md 指出:元组类型可以为每个元素附带可选的标签(labels)或名称,这些标签用于提升可读性和编辑器提示,不会影响你能对其执行的操作——也就是说,带标签与不带标签的元组在结构上是兼容的。
type T = string; type Tuple1 = [T, T]; // 匿名元组:两个 string type Tuple2 = [a: T, b: T]; // 命名元组:两个 string,带标签 a、b type Tuple3 = [a: T, T]; // 命名元组 + 匿名元组混合Tuple3展示了 TypeScript 允许"命名元素与匿名元素共存"的混合写法。命名元组最大的价值体现在编辑器与文档层面:IDE 悬浮提示、函数签名、tsc报错信息都会直接显示a/b这样的标签,帮助开发者理解每个位置的含义,而无需去数索引。这在函数参数(rest 元组)和返回值解构场景中尤其提升可读性:
type HttpResponse = [status: number, body: string, headers: Record<string, string>]; function request(): HttpResponse { return [200, 'ok', { 'content-type': 'application/json' }]; } const [status, body, headers] = request(); // 编辑器会提示每个变量的语义标签进阶三:可变元组类型(Variadic Tuple Types)
如果只有固定长度元组,表达"头部固定、尾部不定"或"拼接两个元组"这类高阶类型操作会非常困难。仓库 pt-br/book/others.md 的"Tipos de Tupla Variádicos"一节指出,TypeScript4.0引入了可变元组类型(Variadic Tuple Types):
"variádico"(可变)意为不定元数(accepts a variable number of arguments)。
可变元组是"格式尚未完全确定"的元组,通过泛型参数在实例化时展开。文档示例:
type Bar<T extends unknown[]> = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number]可以看到,元组的最终形态完全由传入的泛型T决定。可变元组还支持多个泛型参数,并允许 rest 元素出现在元组的任意位置:
type Bar<T extends unknown[], G extends unknown[]> = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean]由此带来的两个能力(原文档明确列出):
- 元组语法中的展开(spread)现在可以是泛型的,从而可以在不知道具体类型的情况下,对元组和数组做高阶操作;
- rest 元素可以出现在元组中的任何位置。
一个结合泛型展开实现"类型安全拼接"的完整示例:
type Items = readonly unknown[]; function concat<T extends Items, U extends Items>( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // 返回类型 [1, 2, 3, "4", "5", "6"]常见陷阱与注意事项
1. 元组不防越界索引
元组约束了"声明时的长度",但通过索引访问时,TypeScript 对越界索引的检查依赖编译选项与版本行为。更稳妥的做法是:在访问元组元素前先解构,让类型系统直接给出确定类型,避免p[2]这类"可能不存在"的访问。
2. 别把元组和数组混用
[string, number]与(string | number)[]在运行时都是数组,但在类型层面完全不同:前者对长度和逐位类型都有限制,后者只要求每个元素属于联合类型。为函数参数选择错误的一方,要么丢失位置语义,要么导致多余的长度校验报错。
3. 需要只读时优先readonly元组
如果元组在声明后不应被修改,应显式使用readonly [number, number]或借助as const。仓库 pt-br/book/primitive-types.md 同时演示了这两种只读形态:
const x: readonly string[] = ['a', 'b']; // 只读数组 const y: ReadonlyArray<string> = ['a', 'b']; // 等价写法只读元组的语法与只读数组一致,均以readonly关键字开头(readonly [string, number]),且不接受push等破坏性操作。
小结
- 元组类型以"固定长度 + 逐位类型 + 位置语义"为特征,是表示坐标、键值对、多返回值等异质定长数据的首选;核心示例
type Point = [number, number]出自 pt-br/book/tuple-type-anonymous.md; - 固定长度元组通过
as const或显式类型标注锁定长度,杜绝意外的push等修改(见 fixed-length-tuple.md); - 命名元组为元素添加标签,只改善可读性与工具提示,不改变结构行为(见 named-tuple-type-labeled.md);
- 只读元组(
readonly [T, ...])提供不可变性保障(见 primitive-types.md); - 可变元组类型(TypeScript 4.0+)用泛型展开让元组在"定长约束"之上获得组合与拼接能力,rest 元素可出现在任意位置(见 others.md)。
若想继续深入,可在仓库中按序阅读这些相邻章节,形成对 TypeScript 元组体系的完整认知:匿名元组 → 命名元组 → 固定长度元组 → 元组与联合/交集类型的交互 → 可变元组类型。
- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
相关推荐
The Concise TypeScript Book 精读:TypeScript 匿名元组类型(Tuple Type)实战指南
The Concise TypeScript Book 精读:TypeScript 匿名元组类型(Tuple Type)实战指南 本篇指南以《The Conci
文档教程《The Concise TypeScript Book》解读:TypeScript 匿名元组类型(Anonymous Tuple Type)完整实战指南
《The Concise TypeScript Book》解读:TypeScript 匿名元组类型(Anonymous Tuple Type)完整实战指南 本篇
文档教程The Concise TypeScript Book 第 29 章精读:TypeScript 匿名元组类型(Tuple Type)的完整实战指南
The Concise TypeScript Book 第 29 章精读:TypeScript 匿名元组类型(Tuple Type)的完整实战指南 元组类型(T
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考