Взгляните на определение типа Array.prototype.reduce
, которое используется в данном случае (первый вызов к нему):
reduce(callbackfn: (prev: T, curr: T) => T): T;
T
- тип элемента массива. Поскольку ваш вызов работает на string[][]
или Array<string[]>
, T
является string[]
. Таким образом, возвращаемое значение этого запроса на уменьшение также должно быть string[]
. Так что, судя по приведенным здесь типам, все кажется правильным.
Но это, очевидно, не то, что происходит, когда вы запускаете код. Возвращаемое значение является фактическим string[][]
. Когда вы отладите свой код, вы увидите, что типы будут фактически меняться во время выполнения.
Причина этого в том, как reduce
работает, если не передать начальное значение:
Если начальное значение не указано, будет использоваться первый элемент в массиве.
Таким образом, первое значение для opt1
будет первым элементом optionGroups
a string[]
. Затем, после запуска внутреннего кода, результат на самом деле равен string[][]
. Вы можете легко подтвердить это, сделав типы явными внутри:
const combinations = optionGroups.reduce((opt1, opt2) => {
const result = opt1.reduce((acc, option1) => {
return acc.concat(opt2.map(option2 => {
return (<string[]>[]).concat(option1, option2);
}));
}, <string[][]>[]);
return <any>result;
});
Итак, проблема в том, что result
- это string[][]
, но внешнее уменьшение все равно принимает string[]
для предыдущего значения (из-за его статического типа). Таким образом, вам придется использовать другое определение типа для reduce
, которое допускает другой тип для предыдущего значения и текущего значения:
reduce<U>(callbackfn: (prev: U, curr: T) => U, initialValue: U): U;
К сожалению, здесь начальное значение является обязательным, поэтому вам придется его передать. Вы не можете передать null
или даже пустой массив здесь, так как это нарушит вашу логику, поэтому вам придется немного изменить свою логику, чтобы по-разному обрабатывать первую итерацию. Например, вот так:
const combinations = optionGroups.reduce<string[][]>((opt1, opt2) => {
if (opt1 === null) {
return opt2.map(option2 => [option2]);
}
return opt1.reduce((acc, option1) => {
return acc.concat(opt2.map(option2 => {
return (<string[]>[]).concat(option1, option2);
}));
}, <string[][]>[]);
}, null);