Context
In an attempt to enhance code readability and maintainability, I am exploring the replacement of a complex overloaded function with rest parameters using labeled tuple elements.
Original snippet
Here's a simplified version of the existing overloaded function:
function doSomething(arg1: string, arg2: number):void;
function doSomething(arg1: string, arg2: number, arg3: unknown[]):void;
function doSomething(arg1: string, arg2: number, arg3: boolean):void;
function doSomething(arg1: string, arg2: number, arg3: unknown[], arg4: boolean):void {
// ...implementation
}
The third and fourth arguments are optional, but when provided, their order can be:
arg3: unknown[]
arg3: unknown[], arg4: boolean
arg3: boolean
Tried workaround
To facilitate working with tuples within utility types, I devised a technique of creating populated tuple elements first and then dynamically adjusting their types based on input variables. The goal is to filter out any elements that resolve to never
in the end result.
type CalcAdditionArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> = [
myArg3: ThirdArgType extends [] ? never : ThirdArgType,
myArg4 : [FourthArgType] extends [boolean] ? FourthArgType extends true ? true : never : never
]
type GetAdditionalArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> =
FilterType<CalcAdditionArgs<ThirdArgType, FourthArgType>, never>
FilterType
has been derived from a modified version of tuple filtering utility found at this link
type FilterType<T extends unknown[], U = undefined> =
(T extends [] ?
[] :
(T extends [infer H, ...infer R] ?
([H] extends [U] ?
FilterType<R, U> :
[H, ...FilterType<R, U>]) :
T
)
);
For clarity, below is the function utilizing these types:
function execute<
ThirdArgType extends unknown[] = [],
FourthArgType extends boolean = false
>(
arg1: string,
arg2: number,
...args:GetAdditionalArgs<ThirdArgType, FourthArgType>
): void {
// implementation here
}
Issue Faced
Upon review, it was noticed that the custom labels assigned to tuple elements were being stripped off by FilterType
. An investigation is needed to rectify this behavior while sticking with the current approach.
- Can anyone pinpoint why tuple element labels are being erased by
FilterType
, and is there a solution available within the existing framework?
Alternatively,
- Is there a simpler or more effective solution to achieve the desired outcome?
Resolution
Acknowledging assistance received from @captain-yossarian and insights shared by @ford04 in a related Stack Overflow post (), I have reevaluated my method by leveraging rest parameters:
type Append<E extends [unknown], A extends unknown[]> = [...A, ...E]
type GetMyArrayArg<T extends unknown[]> = [myArrayArg: T extends [] ? never : T]
type GetMyBooleanArg<T extends boolean> = [myBooleanArg : [T] extends [boolean] ? T extends true ? true : never : never]
type AddParameter<T extends [unknown], U extends unknown[] = []> =
T extends [] ?
U :
T extends [infer H] ?
[H] extends [never] ?
U :
Append<T, U> :
U
type GetAdditionalArgs<ThirdArgType extends unknown[], FourthArgType extends boolean> =
AddParameter<
GetMyBooleanArg<FourthArgType>,
AddParameter<
GetMyArrayArg<ThirdArgType>>
>
type a = GetAdditionalArgs<[], false>; // []
type b = GetAdditionalArgs<[], true>; // [myBooleanArg: true]
type c = GetAdditionalArgs<[string, number], false>; // [myArrayArg: [string, number]]
type d = GetAdditionalArgs<[string, number, Function], true>; // [myArrayArg: [string, number, Function], myBooleanArg: true]