Let's start with a simple function that takes a tuple as its first argument and a function whose arguments are elements of the tuple that are not null as its second argument:
let first: number | null | undefined;
let last: number | null | undefined;
let before: Date | null | undefined;
let after: Date | null | undefined;
function iff<T extends any[]>(...arr: [T, (...args: NonNullableArray<T>) => any]): void {}
iff([first, last], (first, last) => null); // (first, last) is (number, number)
Now, let's try to extend this functionality to accept an array of arguments where each argument matches the signature of the previous function (ignoring non-nullable part for now).
function multipleIfs<
A extends any[] & {
[k in ArrayKeys<A>]: [A[k][0], (args: A[k][0]) => any];
}
>(arg: A): void {}
multipleIfs([
[[first, last], ([first, last]) => null], // (first, last) is (any, any)
[[after, before], ([after, before]) => null], // (after, before) is (any, any)
]);
// More function calls...
The implementation has its limitations and doesn't always work perfectly. I couldn't find a better method programmatically, but I did come up with another approach for smaller arrays:
type IffArgs<T extends Tuple> = [T, (...args: NonNullableArray<T>) => any];
function multipleIfs3<
T0 extends Tuple,
T1 extends Tuple,
T2 extends Tuple,
T3 extends Tuple,
T4 extends Tuple,
T5 extends Tuple,
T6 extends Tuple,
T7 extends Tuple,
T8 extends Tuple,
T9 extends Tuple
>(
...arr: [
IffArgs<T0>,
IffArgs<T1>?,
IffArgs<T2>?,
IffArgs<T3>?,
IffArgs<T4>?,
IffArgs<T5>?,
IffArgs<T6>?,
IffArgs<T7>?,
IffArgs<T8>?,
IffArgs<T9>?,
never?
]
): void {}
// Example call for multipleIfs3 function.
If you have any ideas on how to improve this code, feel free to share!