I cannot convert that piece of code from Javascript to Typescript.
The problem is converting the …spread operator.
function calculateCombinations(first, next, ...rest) {
if (rest.length) {
next = calculateCombinations(next, ...rest);
}
return first.flatMap(a => next.map(b => [a, b].flat()));
}
a1 = ['A', 'B']
a2 = ['+', '']
a3 = ['1', '2']
a4 = ['X', 'Y', 'Z']
// Show possibile combinations
calculateCombinations(a1, a2, a3, a4); // give me an array of 24 combinations
Attempt to conversion to TS:
function calculateCombinationsTS(first: any[], next: any[], ...rest: any[]) {
if (rest.length) {
next = calculateCombinationsTS(next, ...rest);
}
return first.flatMap(a => next.map(b => [a, b].flat()));
}
TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
If I change
next = calculateCombinationsTS(next, ...rest);
to
next = calculateCombinationsTS(next, rest);
the function give me a wrong result, because rest is passed as array of array instead of a list of arguments
Example of output:
nCombo = (a1 * a2 * a3 * a4) = 24 possibilities
[
[ 'A', '+', '1', 'X' ], [ 'A', '+', '1', 'Y' ],
[ 'A', '+', '1', 'Z' ], [ 'A', '+', '2', 'X' ],
[ 'A', '+', '2', 'Y' ], [ 'A', '+', '2', 'Z' ],
[ 'A', '', '1', 'X' ], [ 'A', '', '1', 'Y' ],
[ 'A', '', '1', 'Z' ], [ 'A', '', '2', 'X' ],
[ 'A', '', '2', 'Y' ], [ 'A', '', '2', 'Z' ],
[ 'B', '+', '1', 'X' ], [ 'B', '+', '1', 'Y' ],
[ 'B', '+', '1', 'Z' ], [ 'B', '+', '2', 'X' ],
[ 'B', '+', '2', 'Y' ], [ 'B', '+', '2', 'Z' ],
[ 'B', '', '1', 'X' ], [ 'B', '', '1', 'Y' ],
[ 'B', '', '1', 'Z' ], [ 'B', '', '2', 'X' ],
[ 'B', '', '2', 'Y' ], [ 'B', '', '2', 'Z' ]
]
>Solution :
There are two issues:

If you want to pass a series of array arguments for your rest parameter, you rest parameter must be an array of arrays, not just an array. So:
...rest: any[][]
. TypeScript isn’t complaining about that because you can use an array forany
, but it’s a more accurate reflection of what you’re doing. 
TypeScript can’t tell that the call
calculateCombinationsTS(next, ...more);
will pass an argument fornext
to the function, even though you have a check onrest.length
and so you know it will. You need to call the function with two discrete arguments, followed (optionally) by spreading an array. You can fix that by grabbing the first element from therest
parameter and passing it explicitly:if (rest.length) { const [first, ...more] = rest; next = calculateCombinationsTS(next, first, ...more); }
In theory, that might add overhead. In practice, I suspect the overhead will be sufficiently optimized to not be an issue.
Or, since you know the call is correct (because you know
rest
has at least one element in it), you could silence the error with a@tsignore
, but I try to avoid those where I can.
Complete function fixing both issues:
function calculateCombinationsTS(first: any[], next: any[], ...rest: any[][]) {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^
if (rest.length) {
const [first, ...more] = rest; // ***
next = calculateCombinationsTS(next, first, ...more); // ***
}
return first.flatMap((a) => next.map((b) => [a, b].flat()));
}
Runnable playground example generating the 72 combinations from your example