I am working on creating a function that constructs an array from the input arguments, allowing for only one type or arrays of that same type. Essentially like (...items)=>items.flat(1)
, but with type safety and generics. It seems crucial to ensure that the collected type is not an array itself, as using .flat()
would mix up the values in the output.
The desired function signature, using a generic T
instead of number
:
function arrayOfNumber(...items: (number | number[])[]): number[] {
return items.flat(1);
}
However, when attempting to replace number
with a generic type, it encounters an issue:
function arrayOfGenericFailure<T>(...items: (T | T[])[]): T[] {
return items.flat(1);
}
// Error:
// Type '(T | (T extends readonly (infer InnerArr)[] ? InnerArr : T))[]' is not assignable to type 'T[]'.
// Type 'T | (T extends readonly (infer InnerArr)[] ? InnerArr : T)' is not assignable to type 'T'.
// 'T' could be instantiated with an arbitrary type which could be unrelated to 'T | (T extends readonly (infer InnerArr)[] ? InnerArr : T)'.(2322)
My interpretation is that the infer InnerArr
part attempts to resolve the issue with the types from the previous array mention; please correct me if I misunderstood.
An almost there solution:
type NotArray<T> = Exclude<T, Array<any>>;
export function arrayOfX<T>(...items: (NotArray<T> | NotArray<T>[])[]): T[] {
return items.reduce((acc, val) => acc.concat(val), [] as T[]);
}
This option passes type checks, though it feels messy and restricts argument types rather than the generic itself. Is there a cleaner approach to achieve this?