ITADN

Prevent stack overflow in flatten for deeply nested arrays

#1703Openbyungsker 创建于 2026-05-03
B
byungskercommented
### Problem `flatten` with `Infinity` depth uses recursion internally, making it vulnerable to stack overflow on deeply nested arrays: ```typescript import { flatten } from 'es-toolkit'; const deep = [1]; for (let i = 0; i < 10000; i++) { deep[0] = [deep[0]]; } flatten(deep, Infinity); // RangeError: Maximum call stack size exceeded ``` While 10,000+ depth is uncommon, it represents a **reliability concern**: - Using `Infinity` implies "flatten all levels," but the recursive implementation can't guarantee that - Unexpected deeply nested data (malicious input, serialization edge cases) can crash the process An iterative approach would make `flatten` robust regardless of nesting depth. ### Proposed Solution Convert to an iterative approach using an explicit stack: ```typescript export function flatten<T, D extends number>(arr: readonly T[], depth = 1): Array<FlatArray<T[], D>> { const flooredDepth = Math.floor(depth); if (flooredDepth < 1) return Array.from(arr) as Array<FlatArray<T[], D>>; const result: Array<FlatArray<T[], D>> = []; const stack: [readonly unknown[], number, number][] = [[arr, 0, flooredDepth]]; while (stack.length > 0) { const [current, index, depthRemaining] = stack[stack.length - 1]!; if (index >= current.length) { stack.pop(); continue; } const item = current[index]; stack[stack.length - 1]![1]++; if (depthRemaining > 1 && Array.isArray(item)) { stack.push([item, 0, depthRemaining - 1]); } else { result.push(item as FlatArray<T[], D>); } } return result; } ``` ### Benchmark Results | Nesting Depth | Recursive (old) | Iterative (new) | Winner | |--------------|----------------|-----------------|--------| | depth 3 | 4,571,786 ops/s | 3,221,234 ops/s | Recursive (1.42x) | | depth 5 | 3,856,432 ops/s | 2,489,123 ops/s | Recursive (1.55x) | | depth 100 | 775,432 ops/s | 805,671 ops/s | Iterative (1.04x) | | depth 1000 | 75,834 ops/s | 80,833 ops/s | Iterative (1.07x) | **Trade-off**: Slightly slower for shallow nesting (depth < 10), but faster for deep nesting and eliminates stack overflow risk entirely. ### Test Added ```typescript it('should handle deeply nested arrays without stack overflow', () => { const createNestedArray = (depth: number): unknown[] => { if (depth === 0) return [1]; return [createNestedArray(depth - 1)]; }; const arr = createNestedArray(10000); expect(() => flatten(arr, Infinity)).not.toThrow(); }); ``` ### Checklist - [x] Added tests for deeply nested arrays - [x] Verified all existing tests pass - [x] Benchmarked performance across different nesting depths
2 条评论