You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript条件类型:如何正确获取嵌套Iterable类型?

双层嵌套泛型类型推断的差异机制解析

想要定义一个类型,从Iterable<Iterable<A>>结构中提取出内部的A类型。但两种不同的类型定义方式得到了截然不同的结果:

const arr = [
  [1, 2, 3],
  [4, 5, 6],
]

type GoodNestedIterableType<A> = A extends Iterable<infer B> 
  ? B extends Iterable<infer C>
    ? C : never
  : never
type GoodExtractedType = GoodNestedIterableType<typeof arr> // number

type BadNestedIterableType<A> = A extends Iterable<Iterable<infer B>>
  ? B
  : never
type BadExtractedType = BadNestedIterableType<typeof arr> // unknown

GoodExtractedType能正确推断出number,但BadExtractedType却得到了unknown。而当直接传入Iterable<Iterable<number>>时,BadNestedIterableType又能正常提取number。


核心机制:条件类型的泛型推断顺序与匹配逻辑

这个差异源于TypeScript处理嵌套泛型约束推断和分步泛型推断的逻辑不同:

  1. 分步推断(GoodNestedIterableType)的工作方式
    当使用两层嵌套的条件类型时:
  • 第一步,TypeScript将typeof arr(即number[][],等价于Array<Array<number>>)与Iterable<infer B>匹配,提取出B为Array<number>(因为Array<T>实现了Iterable<T>,其迭代元素类型就是数组的元素类型)。
  • 第二步,再将B(Array<number>)与Iterable<infer C>匹配,此时TypeScript识别到Array<number>同样实现了Iterable<number>,因此提取出C为number。

这种分步处理的方式,让TypeScript可以逐层解析嵌套结构,每一步都单独处理一个Iterable约束,自然能正确提取最内层的类型。

  1. 一次性嵌套推断(BadNestedIterableType)的局限
    当直接使用A extends Iterable<Iterable<infer B>>时,TypeScript需要一次性将输入类型与整个嵌套的Iterable<Iterable<infer B>>结构匹配:
  • typeof arr是Array<Array<number>>,它实现的是Iterable<Array<number>>,而非直接的Iterable<Iterable<number>>。
  • TypeScript在这种一次性匹配场景下,不会自动将内层的Array<number>向上转换为它所实现的Iterable<number>接口来完成推断。它只会检查Array<number>是否直接匹配Iterable<infer B>的结构,而由于Array是具体类型,Iterable是接口,这种直接匹配失败,最终只能将B推断为unknown。
  1. 直接传入Iterable<Iterable<number>>时的例外情况
    当输入类型就是精确的Iterable<Iterable<number>>时,TypeScript可以直接将其与Iterable<Iterable<infer B>>匹配,无需处理具体类型到接口的适配,因此能正确提取B为number。

扩展到非Iterable场景

这种逻辑不限于Iterable接口,所有涉及具体类型实现嵌套接口的场景都会出现类似现象。比如自定义容器接口:

interface Container<T> { content: T }
class Box<T> implements Container<T> {
  constructor(public content: T) {}
}

const nestedBox = new Box(new Box(123));

// 分步推断:正确提取number
type GoodExtract<T> = T extends Container<infer U> 
  ? U extends Container<infer V> ? V : never 
  : never;
type GoodResult = GoodExtract<typeof nestedBox>; // number

// 一次性嵌套推断:得到unknown
type BadExtract<T> = T extends Container<Container<infer V>> ? V : never;
type BadResult = BadExtract<typeof nestedBox>; // unknown

这里Box<Box<number>>实现了Container<Box<number>>,但一次性匹配Container<Container<infer V>>时,TypeScript不会自动将Box<number>转换为Container<number>来完成推断,因此只能得到unknown。

内容的提问来源于stack exchange,提问作者Sidd Singal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 06:13:23