ts-toolbelt中的数组扁平化:UnNest与Flatten的实现差异

ts-toolbelt中的数组扁平化:UnNest与Flatten的实现差异

【免费下载链接】ts-toolbelt 👷 TypeScript's largest type utility library 【免费下载链接】ts-toolbelt 项目地址: https://gitcode.com/gh_mirrors/ts/ts-toolbelt

在处理数组数据时,扁平化操作是常见需求。TypeScript类型工具库ts-toolbelt提供了两种数组扁平化类型工具:UnNest和Flatten。本文将深入分析这两个工具的实现差异、使用场景及性能考量,帮助开发者在实际项目中正确选择合适的工具。

功能定位与核心差异

UnNest和Flatten均位于ts-toolbelt的List模块中,用于处理数组类型的扁平化,但设计目标截然不同:

  • UnNest:仅执行单层扁平化,将数组中的嵌套数组元素展开一层
  • Flatten:执行多层递归扁平化,将数组中所有层级的嵌套数组完全展开

实现文件对比

工具实现文件核心逻辑
UnNestsources/List/UnNest.ts遍历数组,仅展开直接嵌套的数组元素
Flattensources/List/Flatten.ts递归调用UnNest,直至所有层级嵌套数组展开

实现原理深度解析

UnNest的单层展开机制

UnNest通过两种策略实现单层扁平化:

  1. 宽松模式(UnNestLoose):将数组元素转为联合类型后重新构造数组
type UnNestLoose<L extends List> =
  (UnionOf<L> extends infer UL    
  ? UL extends unknown            
    ? UL extends List             
      ? UnionOf<UL>               
      : UL                        
    : never
  : never
  )[] & {}                        
  1. 严格模式(UnNestStrict):通过迭代器逐个处理数组元素
type UnNestStrict<L extends List, LN extends List = [], I extends Iteration = IterationOf<0>> = {
  0: UnNestStrict<L, Flatter<L, LN, I>, Next<I>>
  1: LN
}[Extends<Pos<I>, Length<L>>]

测试用例验证了UnNest的单层展开特性:

// 仅展开第一层数组,深层嵌套数组保持不变
check<T.UnNest<[[1], [2], [3, [4]]]>, [1, 2, 3, [4]], Test.Pass>()

Flatten的多层递归展开

Flatten基于UnNest实现多层递归扁平化,核心逻辑在__Flatten类型中:

type __Flatten<L extends List, LO extends List, strict extends Boolean, limit extends Iteration, I extends Iteration = IterationOf<0>> = {
  0: __Flatten<_UnNest<L, strict>, L, strict, limit, Next<I>>
  1: L
}[Or<Equals<L, LO>, Extends<limit, I>>]

该实现包含两个终止条件:

  • 数组不再变化(Equals<L, LO>
  • 达到指定迭代次数限制(Extends<limit, I>

测试用例展示了多层展开效果:

type T_FLATTEN = [1, 12, [2, [3, [4, [5, [6, [7, [8, [9, 92?]]]]]]]]];
// 完全展开所有层级嵌套
check<T.Flatten<T_FLATTEN>, [1, 12, 2, 3, 4, 5, 6, 7, 8, 9, 92] | [1, 12, 2, 3, 4, 5, 6, 7, 8, 9, undefined], Test.Pass>()

实用对比与场景选择

数据结构对比

以下是两种工具处理相同输入的结果对比:

// 输入类型
type NestedArray = [1, [2, [3, [4]]], 5]

// UnNest结果:仅展开一层
type UnNestResult = UnNest<NestedArray>; // [1, 2, [3, [4]], 5]

// Flatten结果:完全展开所有层级
type FlattenResult = Flatten<NestedArray>; // [1, 2, 3, 4, 5]

性能与限制考量

维度UnNestFlatten
类型计算复杂度O(n) - 线性遍历O(n^d) - 随嵌套深度指数增长
最大嵌套深度无限制(仅处理一层)默认限制为10层
类型推断清晰度高 - 保留深层结构低 - 复杂嵌套可能导致类型模糊

典型应用场景

选择UnNest的场景

  • 处理树形结构的第一层子节点
  • 展平API响应中的分页数据数组
  • 处理仅一层嵌套的数组结构

选择Flatten的场景

  • 处理多维数据统计(如矩阵转列表)
  • 展平嵌套的评论回复列表
  • 预处理需要一维数组输入的算法

使用指南与最佳实践

基础使用示例

// 导入工具
import { UnNest, Flatten } from 'ts-toolbelt'

// UnNest使用示例
type Users = [
  { id: 1, posts: [Post] },
  { id: 2, posts: [Post, Post] }
]
// 提取所有用户的帖子(保留帖子数组)
type UserPosts = UnNest<{ [K in keyof Users]: Users[K]['posts'] }[number][]>

// Flatten使用示例
type CategoryTree = [
  '电子产品', 
  ['手机', ['苹果', '华为']], 
  ['电脑', ['笔记本', '台式机']]
]
// 提取所有叶子分类
type AllCategories = Flatten<CategoryTree>

高级配置选项

两种工具均支持严格模式(strict)配置:

// 严格模式:保留元组特性和可选元素
type StrictUnNest = UnNest<[[1], [2, 3?]], 1> // [1, 2, 3?]

// 宽松模式:转为普通数组
type LooseUnNest = UnNest<[[1], [2, 3?]], 0> // (1 | 2 | 3 | undefined)[]

// 限制最大展开深度为2层
type LimitedFlatten = Flatten<DeepNestedArray, 1, 2>

测试验证与边界情况

ts-toolbelt的测试文件tests/List.ts包含了丰富的验证用例:

UnNest边界测试

// 处理混合类型数组
check<T.UnNest<number[][][] | number[]>, number[][] | number[], Test.Pass>()

// 处理any类型
check<T.UnNest<any>, any[], Test.Pass>()

Flatten边界测试

// 处理空数组
check<T.Flatten<[]>, [], Test.Pass>()

// 处理只读数组
check<T.Flatten<readonly [1, 2, 42]>, [1, 2, 42], Test.Pass>()

// 处理可选元素
check<T.Flatten<[1, 2?]>, [1, undefined] | [1, 2], Test.Pass>()

总结与选型建议

UnNest和Flatten作为ts-toolbelt中处理数组扁平化的核心工具,各自解决不同场景的问题:

  • 当需要保留深层嵌套结构时,选择UnNest,避免过度扁平化
  • 当需要完全展平多维数组时,选择Flatten,并注意设置合理的深度限制
  • 处理复杂嵌套结构时,建议先使用UnNest逐层展开,结合类型守卫确保类型安全

实际项目中,可通过tests/List.ts中的测试用例进一步了解两种工具的行为特性,确保在类型设计阶段做出最优选择。

【免费下载链接】ts-toolbelt 👷 TypeScript's largest type utility library 【免费下载链接】ts-toolbelt 项目地址: https://gitcode.com/gh_mirrors/ts/ts-toolbelt

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值