Arrays typescript中递归函数中嵌套数组的正确类型

Arrays typescript中递归函数中嵌套数组的正确类型,arrays,typescript,recursion,types,Arrays,Typescript,Recursion,Types,我正在尝试实现一个函数flattedeep,它与typescript中的lodash非常相似,但是我在类型定义方面遇到了一些问题,下面是我的代码: 导出接口DeepArray扩展数组{} 导出常量flattedeep=(数组:DeepArray):T[]=>{ 返回[].concat(…array.map(arr=>{ if(数组isArray(arr)){ 返回深度(arr) }否则{ 返回arr } })) } Flattedeep([1[2[3[4]],5]])//应该返回[1,2,3,4

我正在尝试实现一个函数
flattedeep
,它与typescript中的lodash非常相似,但是我在类型定义方面遇到了一些问题,下面是我的代码:

导出接口DeepArray扩展数组{}
导出常量flattedeep=(数组:DeepArray):T[]=>{
返回[].concat(…array.map(arr=>{
if(数组isArray(arr)){
返回深度(arr)
}否则{
返回arr
}
}))
}
Flattedeep([1[2[3[4]],5]])//应该返回[1,2,3,4,5]
到目前为止,它看起来不错,当我测试它时,功能本身是正常的,但是
flattedeep()
函数推断出错误的返回类型:

const arr=[1,2,3,4,5]]
常数展平DARR=展平深度(arr)
//推断类型为:
//常数展平DARR:(数字|(数字|)(数字|数字[])[])[])[]))[]
//但它应该是数字[]或数组?因为最终的结果是一维数组

那么我如何解决这个问题来提供正确的类型呢?

问题在于,由于
DeepArray
是递归类型,并且在结构上与
(T | DeepArray)[
相同,因此没有唯一的
T
对应于
DeepArray
。例如,类型
DeepArray
(数字| DeepArray)[
兼容,因此与
(数字|(数字| DeepArray)[])[]
兼容

因此,当编译器试图为某些
T
(number | number[])[]
等类型解释为
DeepArray
时,不能保证推断
number
。它完全可以自由选择
number | number[]
,或者
number |(number | number[])[]
,或者各种各样的东西,只要它为
T
选择的任何东西允许
(number | number[])[
DeepArray
兼容。因此,即使
flattedeep()
的实现确实使值变平,您也不能指望推理来“展平”
t

declare const flattenDeepOrig: <T>(array: DeepArray<T>) => T[]
const oops = flattenDeepOrig([1, [2, [3, [4]], 5]]) 
// const oops: (number | (number | (number | number[])[])[])[]
但你可能想让编译器帮你解决这个问题。要做到这一点,最好使用像
(array:T[])=>flattarray[]
这样的类型,用于一些适当的
flattarray
定义,该定义将深度嵌套的数组类型显式折叠为平面类型。这是因为给定一个值
[1,2,3,4,5]
作为
T[]
T
唯一合理的推理候选值是类似于
number |(number |(number | number[])[]
,此时您希望
flattarray
将其显式展平到
number
。所以我们只需要定义
flattarray


不幸的是,
flattarray
的直接定义是循环的,编译器不支持:

type FlattenArrayOops<T> = T extends Array<any> ? FlattenArrayOops<T[number]> : T; // error!
//   ~~~~~~~~~~~~~~~~  <-- Type alias 'FlattenArrayOops' circularly references itself.
type flattarrayops=T扩展了数组,以允许这种循环类型,但目前还没有官方支持的方法来实现这种行为

在这种情况下,我通常会通过手动“展开”定义,将其转换为一个固定深度的版本,来近似圆形类型,如下所示:

type FlattenArray<T> = T extends Array<any> ? FA0<T[number]> : T;
type FA0<T> = T extends Array<any> ? FA1<T[number]> : T;
type FA1<T> = T extends Array<any> ? FA2<T[number]> : T;
type FA2<T> = T extends Array<any> ? FA3<T[number]> : T;
type FA3<T> = T extends Array<any> ? FA4<T[number]> : T;
type FA4<T> = T extends Array<any> ? FA5<T[number]> : T;
type FA5<T> = T extends Array<any> ? FA6<T[number]> : T;
type FA6<T> = T extends Array<any> ? FA7<T[number]> : T;
type FA7<T> = T extends Array<any> ? FA8<T[number]> : T;
type FA8<T> = T extends Array<any> ? FAX<T[number]> : T;
type FAX<T> = T; // bail out

type flattarray=T extensed Array

问题在于,由于
DeepArray
是递归类型的,并且在结构上与
(T | DeepArray)[
相同,因此没有唯一的
T
对应于
DeepArray
。例如,类型
DeepArray
(数字| DeepArray)[
兼容,因此与
(数字|(数字| DeepArray)[])[]
兼容

因此,当编译器试图为某些
T
(number | number[])[]
等类型解释为
DeepArray
时,不能保证推断
number
。它完全可以自由选择
number | number[]
,或者
number |(number | number[])[]
,或者各种各样的东西,只要它为
T
选择的任何东西允许
(number | number[])[
DeepArray
兼容。因此,即使
flattedeep()
的实现确实使值变平,您也不能指望推理来“展平”
t

declare const flattenDeepOrig: <T>(array: DeepArray<T>) => T[]
const oops = flattenDeepOrig([1, [2, [3, [4]], 5]]) 
// const oops: (number | (number | (number | number[])[])[])[]
但你可能想让编译器帮你解决这个问题。要做到这一点,最好使用像
(array:T[])=>flattarray[]
这样的类型,用于一些适当的
flattarray
定义,该定义将深度嵌套的数组类型显式折叠为平面类型。这是因为给定一个值
[1,2,3,4,5]
作为
T[]
T
唯一合理的推理候选值是类似于
number |(number |(number | number[])[]
,此时您希望
flattarray
将其显式展平到
number
。所以我们只需要定义
flattarray


不幸的是,
flattarray
的直接定义是循环的,编译器不支持:

type FlattenArrayOops<T> = T extends Array<any> ? FlattenArrayOops<T[number]> : T; // error!
//   ~~~~~~~~~~~~~~~~  <-- Type alias 'FlattenArrayOops' circularly references itself.
type flattarrayops=T扩展了数组,以允许这种循环类型,但目前还没有官方支持的方法来实现这种行为

在这种情况下,我通常会通过手动“展开”定义,将其转换为一个固定深度的版本,来近似圆形类型,如下所示:

type FlattenArray<T> = T extends Array<any> ? FA0<T[number]> : T;
type FA0<T> = T extends Array<any> ? FA1<T[number]> : T;
type FA1<T> = T extends Array<any> ? FA2<T[number]> : T;
type FA2<T> = T extends Array<any> ? FA3<T[number]> : T;
type FA3<T> = T extends Array<any> ? FA4<T[number]> : T;
type FA4<T> = T extends Array<any> ? FA5<T[number]> : T;
type FA5<T> = T extends Array<any> ? FA6<T[number]> : T;
type FA6<T> = T extends Array<any> ? FA7<T[number]> : T;
type FA7<T> = T extends Array<any> ? FA8<T[number]> : T;
type FA8<T> = T extends Array<any> ? FAX<T[number]> : T;
type FAX<T> = T; // bail out
type flattarray=T扩展数组