Skip to content

Commit 904caec

Browse files
committed
fix: support non-fixed array
1 parent 56e83c1 commit 904caec

File tree

2 files changed

+110
-20
lines changed

2 files changed

+110
-20
lines changed

source/array-flat.d.ts

+63-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,39 @@ import type {Or} from './or';
44
import type {Subtract} from './subtract';
55
import type {UnknownArray} from './unknown-array';
66

7+
/**
8+
* Builds an array by repeating the given array items the specified number of times.
9+
*
10+
* @example
11+
* ```
12+
* type FlatArr0 = BuildRepeatedArray<[number, string], 3>;
13+
* //=> type FlatArr0 = [number, string, number, string, number, string];
14+
* ```
15+
*/
16+
type BuildRepeatedArray<T extends UnknownArray, N extends number, R extends unknown[] = []> =
17+
N extends 0
18+
? R
19+
: BuildRepeatedArray<T, Subtract<N, 1>, [...T, ...R]>;
20+
21+
type ArrayFlatOptions = {
22+
/**
23+
* The number of times to repeat the array items when flattening an non-fixed length array.
24+
*
25+
* @example
26+
* ```
27+
* type FlatArr0 = ArrayFlat<Array<number, string>, 1, { repeat: 3 }>;
28+
* //=> type FlatArr0 = [number?, string?, number?, string?, number?, string?];
29+
* ```
30+
*
31+
* @default 10
32+
*/
33+
repeat: number;
34+
};
35+
36+
type ArrayFlatDefaultOptions = {
37+
repeat: 10;
38+
};
39+
740
/**
841
Creates a new array type by flattening an array to a specified depth.
942
@@ -33,24 +66,40 @@ type FlatArr3 = ArrayFlat<Arr1, PositiveInfinity>;
3366
3467
@category Array
3568
*/
36-
export type ArrayFlat<T, Depth extends number = 1> = InternalArrayFlat<T, Depth>;
69+
export type ArrayFlat<T, Depth extends number = 1, Options extends ArrayFlatOptions = ArrayFlatDefaultOptions> =
70+
InternalArrayFlat<T, Depth, Options>;
3771

3872
// Internal implementation
39-
type InternalArrayFlat<T, Depth extends number = 1, Result extends UnknownArray = [] > =
73+
type InternalArrayFlat<T, Depth extends number = 1, Options extends ArrayFlatOptions = ArrayFlatDefaultOptions, Result extends UnknownArray = []> =
4074
T extends UnknownArray
4175
? T['length'] extends 0
4276
? [...Result, ...T]
4377
: Depth extends 0
4478
? [...Result, ...T]
45-
: T extends readonly [infer ArrayItem, ...infer Last]
46-
? ArrayItem extends UnknownArray
47-
? InternalArrayFlat<Last, Depth, [...Result, ...InternalArrayFlat<ArrayItem, Subtract<Depth, 1>>]>
48-
: InternalArrayFlat<Last, Depth, [...Result, ArrayItem]>
49-
: T extends Array<infer ArrayItem2>
50-
? Or<IsAny<ArrayItem2>, IsNever<ArrayItem2>> extends true
51-
? [...Result, ...ArrayItem2[]] // Return never/any[] when input is never/any[]
52-
: ArrayItem2 extends UnknownArray
53-
? InternalArrayFlat<ArrayItem2, Subtract<Depth, 1>, Result>
54-
: [...Result, ...ArrayItem2[]]
55-
: [...Result, ...T]
56-
: T;
79+
: number extends T['length']
80+
? [
81+
...Result,
82+
...(
83+
T[number] extends UnknownArray
84+
? BuildRepeatedArray<InternalArrayFlat<
85+
number extends T[number]['length'] ? T[number] : Partial<T[number]>,
86+
Subtract<Depth, 1>,
87+
Options
88+
>,
89+
Options['repeat']
90+
>
91+
: T
92+
),
93+
]
94+
: T extends readonly [infer ArrayItem, ...infer Last]
95+
? ArrayItem extends UnknownArray
96+
? InternalArrayFlat<Last, Depth, Options, [...Result, ...InternalArrayFlat<ArrayItem, Subtract<Depth, 1>, Options>]>
97+
: InternalArrayFlat<Last, Depth, Options, [...Result, ArrayItem]>
98+
: T extends Array<infer ArrayItem2>
99+
? Or<IsAny<ArrayItem2>, IsNever<ArrayItem2>> extends true
100+
? [...Result, ...ArrayItem2[]] // Return never/any[] when input is never/any[]
101+
: ArrayItem2 extends UnknownArray
102+
? InternalArrayFlat<ArrayItem2, Subtract<Depth, 1>, Options, Result>
103+
: [...Result, ...ArrayItem2[]]
104+
: [...Result, ...T]
105+
: [];

test-d/array-flat.ts

+47-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {expectType} from 'tsd';
1+
import {expectAssignable, expectType} from 'tsd';
22
import type {ArrayFlat, PositiveInfinity} from '../index';
33

44
// Basic flattening tests
@@ -34,9 +34,7 @@ expectType<ArrayFlat<[{a: number}, [{b: string}]]>>([null! as {a: number}, null!
3434
expectType<ArrayFlat<[1, 2] | [[3, 4]]>>([1, 2] as [1, 2] | [3, 4]);
3535
expectType<ArrayFlat<[1, [2]] | [[3], 4]>>([1, 2] as [1, 2] | [3, 4]);
3636

37-
// Test with rest elements
38-
expectType<ArrayFlat<[...Array<[number, string]>]>>([null! as number, null! as string]);
39-
expectType<ArrayFlat<[1, 2, ...Array<[string, boolean]>]>>([1, 2, null! as string, null! as boolean]);
37+
expectType<ArrayFlat<[...Array<[number, string]>]>>(null! as [number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?]);
4038
expectType<ArrayFlat<[1, [2, ...Array<3>], 4]>>([1, 2, ...(null! as Array<3>), 4]);
4139

4240
// Test with mixed arrays and tuples
@@ -55,6 +53,49 @@ expectType<ArrayFlat<Array<[]>>>([]);
5553
expectType<ArrayFlat<[[]]>>([]);
5654
expectType<ArrayFlat<[[], []]>>([]);
5755
expectType<ArrayFlat<undefined[]>>(null! as undefined[]);
58-
expectType<ArrayFlat<any[]>>(null! as any[]);
5956
expectType<ArrayFlat<unknown[]>>(null! as unknown[]);
60-
expectType<ArrayFlat<never[]>>(null! as never[]);
57+
58+
// Test specifically for non-tuple array handling (Array<T> vs [T, T])
59+
type GenericNumberArrays = number[][];
60+
expectAssignable<ArrayFlat<GenericNumberArrays>>([1, 2, 3, 4, 5]);
61+
62+
// Test for deeply nested non-tuple arrays with specific depths
63+
type DeepGenericArray = number[][][][];
64+
expectAssignable<ArrayFlat<DeepGenericArray, 2>>([[1, 2], [3, 4]]);
65+
expectAssignable<ArrayFlat<DeepGenericArray, 3>>([1, 2, 3, 4]);
66+
67+
// Test for mixed generic arrays and tuples
68+
type MixedGenericAndTuple = Array<[number, string]>;
69+
expectAssignable<ArrayFlat<MixedGenericAndTuple>>([1, 'a', 2, 'b']);
70+
71+
// Test for array with optional elements in nested structure
72+
type NestedOptional = [number, Array<[string?, number?]>];
73+
expectAssignable<ArrayFlat<NestedOptional, 2>>([1, 'a', 2, 'b', 3]);
74+
75+
// Test for arrays with rest elements in nested structure
76+
type NestedRest = [string, Array<[...number[]]>];
77+
expectAssignable<ArrayFlat<NestedRest, 2>>(['a', 1, 2, 3, 4, 5]);
78+
79+
// Test for flattening arrays with union types in nested structures
80+
type NestedUnion = Array<Array<string | number>>;
81+
expectAssignable<ArrayFlat<NestedUnion>>(['a', 1, 'b', 2]);
82+
83+
// Test for empty array in complex structure
84+
type ComplexWithEmpty = [number, Array<[]>, string];
85+
expectAssignable<ArrayFlat<ComplexWithEmpty>>([1, 'string']);
86+
87+
// Test for array with undefined/null elements
88+
type ArrayWithNullish = [number, [undefined, null]];
89+
expectAssignable<ArrayFlat<ArrayWithNullish>>([1, undefined, null]);
90+
91+
// Test for array with mixed depth elements
92+
type MixedDepthArray = [number, string[], [[boolean]]];
93+
expectAssignable<ArrayFlat<MixedDepthArray>>([1, 'a', 'b', [true]]);
94+
95+
// Test for readonly nested arrays with different depths
96+
type ReadonlyNestedComplex = readonly [number, ReadonlyArray<readonly string[]>];
97+
expectAssignable<ArrayFlat<ReadonlyNestedComplex, 2>>([1, 'a', 'b', 'c']);
98+
99+
// Test for recursive flattening with non-array elements
100+
type RecursiveWithNonArray = [number, [string, {a: number}]];
101+
expectAssignable<ArrayFlat<RecursiveWithNonArray>>([1, 'string', {a: 42}]);

0 commit comments

Comments
 (0)