It is doable in TS 4.5 (nightly version) but not in a way you expect.
Thanks to variadic-tuple-types you can do this:
type Reducer<
Arr extends Array<unknown>,
Result extends Array<unknown> = []
> =
(Arr extends []
? Result
: (Arr extends [infer H, ...infer Tail]
? (H extends Array<any>
? Reducer<[...H, ...Tail], Result> : Reducer<Tail, [...Result, H]>) : never
)
)
// [1,2,3]
type Result = Reducer<[[[1], [[[[[[[2]]]]]]]], 3]>
// [1, 2, 3, 4, 5, 6]
type Result2 = Reducer<[[[[[[[[[1]]]]]]]],[[[[[[2,3,4]]]],[[[[5,6]]]]]]]>
How do I use that as a function return value type?
In order to use it with function, you need to convert argument to immutable array:
type Reducer<
Arr,
Result extends ReadonlyArray<unknown> = []
> = Arr extends ReadonlyArray<unknown> ?
(Arr extends readonly []
? Result
: (Arr extends readonly [infer H, ...infer Tail]
? (H extends ReadonlyArray<any>
? Reducer<readonly [...H, ...Tail], Result> : Reducer<Tail, readonly [...Result, H]>) : never
)
) : never
const flatten = <
Elem,
T extends ReadonlyArray<T | Elem>
>(arr: readonly [...T]): Reducer<T> =>
arr.reduce((acc, elem) =>
Array.isArray(elem)
? flatten(elem) as Reducer<T>
: [...acc, elem] as Reducer<T>,
[] as Reducer<T>
)
const result = flatten([[[[[[1]]], 2], 3]] as const)
Playground
You should have also add second argument to reduce method.
More explanation you can find in my article.
If you want to better understand how Reducer utility type work, see this exmaple:
const Reducer = <T,>(Arr: ReadonlyArray<T>, Result: ReadonlyArray<T> = [])
: ReadonlyArray<T> => {
if (Arr.length === 0) {
return Result
}
const [Head, ...Tail] = Arr;
if (Array.isArray(Head)) {
return Reducer([...Head, ...Tail], Result)
}
return Reducer(Tail, [...Result, Head])
}