题目描述 🎯
实现泛型TupleToUnion<T>,它返回元组所有值的联合类型。
type Arr = ['1', '2', '3']
type Test = TupleToUnion<Arr> // '1' | '2' | '3'
题目分析 📝
-
输入:一个元组类型
T -
输出:元组中所有元素的联合类型
-
需要处理的情况:
- 空元组
- 包含不同类型元素的元组
- 单个元素的元组
解题思路 💡
有两种主要的实现思路:
-
使用索引访问类型:
- 利用
T[number]可以获取数组(元组)所有元素的联合类型
- 利用
-
使用
infer递归:- 使用
infer依次提取元组中的元素 - 通过递归构建联合类型
- 使用
代码实现 ⌨️
方法一:索引访问类型
type TupleToUnion<T extends any[]> = T[number]
方法二:infer 递归
type TupleToUnion<T extends readonly any[]> = T extends [infer F, ...infer R] ? F | TupleToUnion<R> : never
题解详情 🔍
方法一:索引访问类型解析
type TupleToUnion<T extends readonly any[]> = T[number]
-
T extends readonly any[]- 限制
T必须是一个只读的数组类型
- 限制
2.T[number]
- 使用数字索引访问类型
TypeScript会自动将数组/元组的所有可能索引值对应的类型联合起来
为什么使用 readonly any[] ?
使用readonly any[]可以接受更多类型的参数:
- 可以接受普通数组/元组
- 可以接受只读数组/元组
- 提供更好的类型兼容性
// 使用 any[]
type TupleToUnion1<T extends any[]> = T[number];
// 使用 readonly any[]
type TupleToUnion2<T extends readonly any[]> = T[number];
// 定义一个只读元组
const tuple = ['1', '2', '3'] as const;
// tuple 的类型是 readonly ['1', '2', '3']
// 使用 any[]
type Test1 = TupleToUnion1<typeof tuple>; // ❌ 类型错误
// The type 'readonly ["1", "2", "3"]' is 'readonly' and cannot be assigned to the mutable type 'any[]'.
// 使用 readonly any[]
type Test2 = TupleToUnion2<typeof tuple>; // ✅ 正常工作
// type Test2 = "1" | "2" | "3"
方法二:infer递归解析
type TupleToUnion<T extends readonly any[]> = T extends [infer F, ...infer R] ? F | TupleToUnion<R> : never
-
T extends [infer F, ...infer R]- 使用
infer提取元组的第一个元素(F)和剩余元素(R)
- 使用
-
F | TupleToUnion<R>- 将第一个元素与递归处理剩余元素的结果联合
-
:never- 当元组为空时返回
never
- 当元组为空时返回
示例分析 🌟
让我们通过一个具体示例来看执行过程:
type Arr = ['1', '2', '3']
type Result = TupleToUnion<Arr>
// 方法一:索引访问类型
type Result1 = Arr[number]
// 直接得到 '1' | '2' | '3'
// 方法二:infer 递归
// 步骤 1:
T extends ['1', '2', '3']
F = '1'
R = ['2', '3']
Result = '1' | TupleToUnion<['2', '3']>
// 步骤 2:
T extends ['2', '3']
F = '2'
R = ['3']
Result = '1' | '2' | TupleToUnion<['3']>
// 步骤 3:
T extends ['3']
F = '3'
R = []
Result = '1' | '2' | '3' | TupleToUnion<[]>
// 步骤 4:
T extends [] = false
Result = '1' | '2' | '3' | never
// 最终得到 '1' | '2' | '3'
测试示例 👀
type TupleToUnion1<T extends readonly any[]> = T[number];
type TupleToUnion2<T extends readonly any[]> =
T extends [infer F, ...infer R]
? F | TupleToUnion2<R>
: never;
// 测试空元组
type EmptyTuple = []
type EmptyResult1 = TupleToUnion1<EmptyTuple> // never
type EmptyResult2 = TupleToUnion2<EmptyTuple> // never
// 测试混合类型元组
type MixedTuple = [string, number, boolean]
type MixedResult1 = TupleToUnion1<MixedTuple> // string | number | boolean
type MixedResult2 = TupleToUnion2<MixedTuple> // string | number | boolean
// 测试单元素元组
type SingleTuple = [string]
type SingleResult1 = TupleToUnion1<SingleTuple> // string
type SingleResult2 = TupleToUnion2<SingleTuple> // string