Skip to content

Commit 56e83c1

Browse files
committed
Add ArrayFlat type
1 parent 063e28d commit 56e83c1

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export type {ArrayValues} from './source/array-values';
129129
export type {ArraySlice} from './source/array-slice';
130130
export type {ArraySplice} from './source/array-splice';
131131
export type {ArrayTail} from './source/array-tail';
132+
export type {ArrayFlat} from './source/array-flat';
132133
export type {SetFieldType} from './source/set-field-type';
133134
export type {Paths} from './source/paths';
134135
export type {AllUnionFields} from './source/all-union-fields';

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ type ShouldBeNever = IfAny<'not any', 'not never', 'never'>;
285285
- [`Includes`](source/includes.d.ts) - Returns a boolean for whether the given array includes the given item.
286286
- [`Join`](source/join.d.ts) - Join an array of strings and/or numbers using the given string as a delimiter.
287287
- [`ArraySlice`](source/array-slice.d.ts) - Returns an array slice of a given range, just like `Array#slice()`.
288+
- [`ArrayFlat`](source/array-flat.d.ts) - Creates a new array type by flattening an array to a specified depth, just like `Array#flat()`.
288289
- [`LastArrayElement`](source/last-array-element.d.ts) - Extracts the type of the last element of an array.
289290
- [`FixedLengthArray`](source/fixed-length-array.d.ts) - Create a type that represents an array of the given type and length.
290291
- [`MultidimensionalArray`](source/multidimensional-array.d.ts) - Create a type that represents a multidimensional array of the given type and dimensions.

source/array-flat.d.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type {IsAny} from './is-any';
2+
import type {IsNever} from './is-never';
3+
import type {Or} from './or';
4+
import type {Subtract} from './subtract';
5+
import type {UnknownArray} from './unknown-array';
6+
7+
/**
8+
Creates a new array type by flattening an array to a specified depth.
9+
10+
Use-case: Flatten an array type to a specified depth.
11+
12+
Like [`Array#flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) but for types.
13+
14+
@example
15+
```
16+
import type {ArrayFlat, PositiveInfinity} from 'type-fest';
17+
18+
type FlatArr0 = ArrayFlat<[[0, 1], [2, 3], [4, 5]]>;
19+
//=> type FlatArr0 = [0, 1, 2, 3, 4, 5];
20+
21+
// Flatten to depth
22+
type Arr1 = [[0, [1, [2, [3, [4, [5]]]]]]];
23+
type FlatArr1 = ArrayFlat<Arr1, 1>;
24+
//=> type FlatArr1 = [0, [1, [2, [3, [4, [5]]]]]];
25+
26+
type FlatArr2 = ArrayFlat<Arr1, 3>;
27+
//=> type FlatArr2 = [0, 1, 2, [3, [4, [5]]]];
28+
29+
// Flatten to depth Infinity
30+
type FlatArr3 = ArrayFlat<Arr1, PositiveInfinity>;
31+
//=> type FlatArr3 = [0, 1, 2, 3, 4, 5];
32+
```
33+
34+
@category Array
35+
*/
36+
export type ArrayFlat<T, Depth extends number = 1> = InternalArrayFlat<T, Depth>;
37+
38+
// Internal implementation
39+
type InternalArrayFlat<T, Depth extends number = 1, Result extends UnknownArray = [] > =
40+
T extends UnknownArray
41+
? T['length'] extends 0
42+
? [...Result, ...T]
43+
: Depth extends 0
44+
? [...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;

test-d/array-flat.ts

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {expectType} from 'tsd';
2+
import type {ArrayFlat, PositiveInfinity} from '../index';
3+
4+
// Basic flattening tests
5+
expectType<ArrayFlat<[]>>([]);
6+
expectType<ArrayFlat<[1, 2, 3]>>([1, 2, 3]);
7+
expectType<ArrayFlat<[[1, 2], [3, 4]]>>([1, 2, 3, 4]);
8+
expectType<ArrayFlat<[1, [2, 3], 4]>>([1, 2, 3, 4]);
9+
expectType<ArrayFlat<[1, [2, [3]], 4]>>([1, 2, [3], 4]);
10+
11+
// Test with explicit depth
12+
// eslint-disable-next-line unicorn/prevent-abbreviations
13+
type Arr = [[0, [1, [2, [3, [4, [5]]]]]]];
14+
expectType<ArrayFlat<Arr, 0>>(null! as Arr);
15+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments
16+
expectType<ArrayFlat<Arr, 1>>([0, [1, [2, [3, [4, [5]]]]]]);
17+
expectType<ArrayFlat<Arr, 2>>([0, 1, [2, [3, [4, [5]]]]]);
18+
expectType<ArrayFlat<Arr, 3>>([0, 1, 2, [3, [4, [5]]]]);
19+
expectType<ArrayFlat<Arr, 4>>([0, 1, 2, 3, [4, [5]]]);
20+
expectType<ArrayFlat<Arr, 5>>([0, 1, 2, 3, 4, [5]]);
21+
expectType<ArrayFlat<Arr, 6>>([0, 1, 2, 3, 4, 5]);
22+
expectType<ArrayFlat<Arr, 7>>([0, 1, 2, 3, 4, 5]);
23+
expectType<ArrayFlat<Arr, PositiveInfinity>>([0, 1, 2, 3, 4, 5]);
24+
25+
// Test with Infinity depth
26+
expectType<ArrayFlat<[1, [2, [3, [4]]]], PositiveInfinity>>([1, 2, 3, 4]);
27+
expectType<ArrayFlat<[[[[[1]]]]], PositiveInfinity>>([1]);
28+
29+
// Test with different element types
30+
expectType<ArrayFlat<[string, [number, boolean]]>>([null! as string, null! as number, null! as boolean]);
31+
expectType<ArrayFlat<[{a: number}, [{b: string}]]>>([null! as {a: number}, null! as {b: string}]);
32+
33+
// Test with union types
34+
expectType<ArrayFlat<[1, 2] | [[3, 4]]>>([1, 2] as [1, 2] | [3, 4]);
35+
expectType<ArrayFlat<[1, [2]] | [[3], 4]>>([1, 2] as [1, 2] | [3, 4]);
36+
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]);
40+
expectType<ArrayFlat<[1, [2, ...Array<3>], 4]>>([1, 2, ...(null! as Array<3>), 4]);
41+
42+
// Test with mixed arrays and tuples
43+
expectType<ArrayFlat<[1, number[], 3]>>([1, ...(null! as number[]), 3]);
44+
expectType<ArrayFlat<[string, [number, ...boolean[]]]>>([null! as string, null! as number, ...(null! as boolean[])]);
45+
46+
// Test with deeply nested structures
47+
expectType<ArrayFlat<[1, [2, [3, [4, [5]]]]], 3>>([1, 2, 3, 4, [5]]);
48+
49+
// Test with readonly arrays
50+
expectType<ArrayFlat<readonly [1, readonly [2, 3]]>>([1, 2, 3]);
51+
expectType<ArrayFlat<readonly [readonly [1, 2], readonly [3, 4]]>>([1, 2, 3, 4]);
52+
53+
// Edge cases
54+
expectType<ArrayFlat<Array<[]>>>([]);
55+
expectType<ArrayFlat<[[]]>>([]);
56+
expectType<ArrayFlat<[[], []]>>([]);
57+
expectType<ArrayFlat<undefined[]>>(null! as undefined[]);
58+
expectType<ArrayFlat<any[]>>(null! as any[]);
59+
expectType<ArrayFlat<unknown[]>>(null! as unknown[]);
60+
expectType<ArrayFlat<never[]>>(null! as never[]);

0 commit comments

Comments
 (0)