Typescript 3.0.1无法理解嵌套的reduce

时间:2018-08-28 17:46:00

标签: typescript types reduce

所以我有这段代码可以找到x个子数组的所有可能组合:

const optionGroups: string[][] = [['Beer', 'Not beer'], ['1', '2', '11']];

const combinations = optionGroups.reduce((opt1, opt2) => {
  return opt1.reduce((acc, option1) => {
    return acc.concat(opt2.map(option2 => {
      return (<string[]>[]).concat(option1, option2);
    }));
  }, []);
});

return combinations; // <-- This is not a string[]

但是Typescript认为返回类型是string[],而实际上却是string[][]

输出为[['1', 'Beer'], ['2', 'Beer']...]

我一辈子都无法弄清楚如何修复字体。您可以自己尝试一下该片段,看看。

即使强制转换也会产生错误:

  

类型'string []'无法转换为类型'string [] []'。类型   'string'与类型'string []'不具有可比性。

有指针吗?

2 个答案:

答案 0 :(得分:1)

看看在这种情况下使用的Array.prototype.reduce的类型定义(第一次调用):

reduce(callbackfn: (prev: T, curr: T) => T): T;

T是数组元素类型。由于您的通话是在string[][]Array<string[]>上进行的,因此Tstring[]。因此,该reduce调用的返回值也必须为string[]。因此,仅从此处的类型来看,一切似乎都是正确的。

但是,运行代码时显然不会发生什么。返回值是实际的string[][]。调试代码时,您会看到类型实际上会在运行时更改。

这样做的原因是reduce在不传递初始值时如何工作:

  

如果未提供初始值,则将使用数组中的第一个元素。

因此opt1的第一个值将是optionGroupsstring[]的第一个元素。然后,在运行内部代码之后,结果实际上是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;
});

因此,现在的问题是resultstring[][],但是外部归约仍然假设先前值是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);

答案 1 :(得分:0)

使用外部约简将optionGroups分解为两个元素是很奇怪的,并且是问题的根源,因为当您在没有初始状态的情况下进行约简时,假定该状态具有相同的状态。类型作为输入的元素,即string[]。外部回调实际上返回string[][],但是由于内部reduce的初始状态[]的类型被扩展为any[]而没有给出任何隐式错误,因此未捕获到不匹配。参见this issue。启用strictNullChecks将禁用这种不良的扩展形式,并为您提供预期的类型错误。