为啥 TypeScript 在使用 concat 减少数组时会推断出“从不”类型?

Posted

技术标签:

【中文标题】为啥 TypeScript 在使用 concat 减少数组时会推断出“从不”类型?【英文标题】:Why does TypeScript infer the 'never' type when reducing an Array with concat?为什么 TypeScript 在使用 concat 减少数组时会推断出“从不”类型? 【发布时间】:2019-06-04 15:20:18 【问题描述】:

代码胜于语言,所以:

['a', 'b', 'c'].reduce((accumulator, value) => accumulator.concat(value), []);

代码很傻,返回一个复制的Array...

TS 抱怨 concat 的参数:TS2345:“字符串”类型的参数不可分配给“ConcatArray”类型的参数。

【问题讨论】:

【参考方案1】:

以上方法都不适合我,即使将 tsconfig.json 文件更改为 "strict": false 并且只能通过以下方式避免破坏应用程序:

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore

【讨论】:

这并不能真正回答问题。如果您有其他问题,可以点击 进行提问。要在此问题有新答案时收到通知,您可以follow this question。一旦你有足够的reputation,你也可以add a bounty 来引起对这个问题的更多关注。 - From Review【参考方案2】:

你应该使用泛型来解决这个问题。

['a', 'b', 'c'].reduce<string[]>((accumulator, value) => accumulator.concat(value), []);

这将设置初始空数组的类型,我认为这是最正确的解决方案。

【讨论】:

【参考方案3】:

避免类型转换的更好解决方案:

accumulator 值键入为string[](并避免在[] 上进行类型转换):

['a', 'b', 'c'].reduce((accumulator: string[], value) => accumulator.concat(value), []);

试试这个解决方案in the typescript playground。

注意事项

    应尽可能避免类型转换,因为您将采用一种类型并将其转换为其他类型。这可能会导致副作用,因为您手动控制将变量强制转换为另一种类型。

    此打字稿错误仅在 strictNullChecks 选项设置为 true 时发生。禁用该选项时,Typescript 错误消失,但这可能不是您想要的。

    我在这里引用了我使用 Typescript 3.9.2 收到的整个错误消息,以便 Google 为正在搜索答案的人找到此线程(因为 Typescript 错误消息有时会因版本而异):

    No overload matches this call.
      Overload 1 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.
     Argument of type 'string' is not assignable to parameter of type 'ConcatArray<never>'.
      Overload 2 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.
     Argument of type 'string' is not assignable to parameter of type 'ConcatArray<never>'.(2769)
    

【讨论】:

这应该是公认的答案。稍微不同的解决方案,使用泛型:['a', 'b', 'c'].reduce&lt;string[]&gt;((accumulator, value) =&gt; accumulator.concat(value), []); 感谢 Andru 的宝贵反馈。我也有同样的问题;所以用谷歌搜索并得出你的正确答案。【参考方案4】:

我相信这是因为[] 的类型被推断为never[],这是必须为空的数组的类型。您可以使用类型转换来解决这个问题:

['a', 'b', 'c'].reduce((accumulator, value) => accumulator.concat(value), [] as string[]);

通常这不是什么大问题,因为 TypeScript 在根据您对空数组的操作找出更好的类型以分配给空数组方面做得不错。但是,由于您的示例正如您所说的那样“愚蠢”,因此 TypeScript 无法进行任何推断,并将类型保留为 never[]

【讨论】:

"为什么 TypeScript 会推断出 'never' 类型?" “因为[] 的类型被推断为never[]”。如果是这样,那么为什么const array = [];array 推断为any[] 类型? 通常,TypeScript 在根据空数组的使用方式推断空数组的正确类型方面做得很好。但是,在这个例子中它不能,因为它在回调的参数或返回类型中没有任何类型,并且它没有使用原始的 ['a', 'b', 'c']用于类型推断的数组。执行const array = []; 之类的操作确实会产生any[] 数组。但是,我认为这是一个明确的例外,以避免混淆人们。如果您执行[].filter(a =&gt; true); 之类的操作,您会看到它推断出never[] 类型而不是any[] 类型。 相关:github.com/Microsoft/TypeScript/issues/18687 这比我意识到的要复杂,但[] 在上下文输入时被推断为某种“扩大any[]”类型......显然以一种不在reduce() 中作为累加器传递时不会发生这种情况。 ?‍♀️ @MattH 是的 TS 无法确定数组的类型,您可以强制转换或将类型作为模板参数传递以减少它的工作原理! 刚找到github.com/microsoft/TypeScript/issues/29795,可能是上述信息的权威来源

以上是关于为啥 TypeScript 在使用 concat 减少数组时会推断出“从不”类型?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Pandas 串联 (pandas.concat) 的内存效率如此之低?

为啥 concat 重新格式化我的标题?

sql:为啥使用'GROUP CONCAT'+'GROUP BY'时查询重复值?

为啥在 Typescript 泛型中使用 '&'

为啥 TypeScript 中的类允许使用鸭子类型

pd.concat() 和 pd.merge() 之间的区别以及为啥我会得到错误的输出?