Iterable<Iterable<T>> 无法确认函数中的泛型 T

Myu*_* Ko 4 generics type-inference typescript

这是我的问题。

const iterable = [[[1,2,3]]]
function flat<T>(t:Iterable<Iterable<T>>):Iterable<T>{
    return [...t][0]
}
const flatted = flat(iterable) //return Iterable<unknown>  
Run Code Online (Sandbox Code Playgroud)

上面的函数不能假设 T 为数字,只需将其断言为未知。此时此刻,我觉得“嗯……泛型中的泛型无法推断?”。但下面的代码chuck效果很好

const iterable = [[[1,2,3]]]
function flat<T>(t:Array<Array<T>>):Array<T>{
    return [...t][0]
}
const flatted = flat(iterable) // omg.. return Array<number> 
Run Code Online (Sandbox Code Playgroud)

const iterable = [[[1,2,3]]]
function flat<T>(t:Iterable<Array<T>>):Array<T>{
    return [...t][0]
}
const flatted = flat(iterable) // also works.. return Array<number> 
Run Code Online (Sandbox Code Playgroud)

它们之间有什么区别?感谢您阅读我的问题。

jca*_*alz 8

哎呀,是的,我发现默认推理的工作深度不够深,无法展开Iterable<Iterable<T>>T. 如果你看看相关库中的类型是如何定义的,这并不奇怪Iterable

interface Iterable<T> {
    [Symbol.iterator](): Iterator<T>;
}
Run Code Online (Sandbox Code Playgroud)

AnIterable<T>有一个符号键控方法,其返回类型为Iterator<T>,其本身定义为:

interface Iterator<T, TReturn = any, TNext = undefined> {
    next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
    return?(value?: TReturn): IteratorResult<T, TReturn>;
    throw?(e?: any): IteratorResult<T, TReturn>;
}
Run Code Online (Sandbox Code Playgroud)

其中所有方法都 return IteratorResult<T, ...>,它被定义为可区分的联合类型

type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
Run Code Online (Sandbox Code Playgroud)

成员

interface IteratorYieldResult<TYield> {
    done?: false;
    value: TYield;
}

interface IteratorReturnResult<TReturn> {
    done: true;
    value: TReturn;
}
Run Code Online (Sandbox Code Playgroud)

其中只有一个具有您要查找的value相关类型的属性。T

因此,要将类型转换XIterable<T>T编译器需要执行类似的操作Extract<ReturnType<ReturnType<X[typeof Symbol.iterator]>['next']>, { done?: false }>['value']

我认为可能存在一些深度限制,之后编译器就会放弃尝试推断事物。您可以看到 inferring TfromIterable<T>有效(可能有大约 5 或 6 层嵌套),但 inferring TfromIterable<Iterable<T>>太深了(10 或 12 层?):

type N = number[][] extends Iterable<infer T> ? T : never; // number[] 
type O = number[][] extends Iterable<Iterable<infer T>> ? T : never; // unknown 
Run Code Online (Sandbox Code Playgroud)

这导致我采取以下解决方法:创建一个类型别名以在 的一层上显式操作Iterable,然后使用该别名两次:

type DeIterable<T extends Iterable<any>> = T extends Iterable<infer U> ? U : never;
Run Code Online (Sandbox Code Playgroud)

你可以看到这个作品:

type Okay = DeIterable<DeIterable<number[][]>>; // number 
Run Code Online (Sandbox Code Playgroud)

现在flat()可以这样定义:

function flat<II extends Iterable<Iterable<any>>>(
  t: II
): Iterable<DeIterable<DeIterable<II>>> {
  return [...t][0]
}
Run Code Online (Sandbox Code Playgroud)

其中输入值是泛型类型II,我们使用DeIterable它两次来获得T您之前想要的:

const flatted = flat(iterable) //return Iterable<number[]>  
Run Code Online (Sandbox Code Playgroud)

现在看起来不错!

Playground 代码链接