TypeScript中flatMap处理嵌套Promise数组的类型推导异常
两类代码的运行差异本质是TypeScript对数组方法的类型推导规则不同
- 先梳理所有代码共用的初始类型链路:
- 初始定义的
x,TypeScript推导出来的类型是string[] - 由三个
x组成的y,推导出来的类型是string[][] - 内层对单个数组元素
a(也就是string[]类型)执行a.map(b => b),返回值是string[],外层包裹的Promise.all(...)会把这个值转成Promise<string[]>类型
- 初始定义的
- 第一份报错代码的完整类型推导路径:
Array.flatMap是纯同步的结构转换方法,运行规则是:遍历数组每一项,把回调返回的同步值打平一层后拼成新数组。这个方法执行的时候根本感知不到回调返回的是Promise,只会把Promise<string[]>当成一个普通的对象元素处理- 因为回调返回的是单个
Promise<string[]>(不是数组结构),flatMap执行完生成的数组类型就是Promise<string[]>[] - 外层套上
Promise.all并await后,所有Promise都会被解包,最终拿到的值的类型是string[][](二维字符串数组) - 这时候直接调用
.includes('a'),二维数组的includes方法要求传入的参数和数组元素类型一致,也就是必须传string[],现在传了单个字符串'a',自然触发类型不匹配的报错
- 第二份可正常运行的代码的完整类型推导路径:
- 把
flatMap换成map后,map同样是纯同步的结构转换方法,运行规则是:直接把回调返回的同步值作为新数组的元素,不做额外打平操作 - map执行完生成的数组类型和之前一致,也是
Promise<string[]>[],经过外层Promise.allawait解包后,拿到的同样是string[][]二维数组 - 这时候显式调用
.flat()方法,把二维数组同步打平成一维的string[],再调用.includes('a'),参数类型和数组元素类型完全匹配,自然可以正常运行
- 把
- 容易踩的认知误区:
- 不要把flatMap的同步打平逻辑和Promise的异步解包逻辑混为一谈。数组的所有内置同步方法,根本不会等待内部的Promise执行完成,也不会对Promise resolve之后的返回结构做打平处理。第一份代码本来想靠flatMap一步完成打平,但实际上等所有Promise resolve完,拿到的还是二维结构的数组,根本没完成打平,自然会出现类型不匹配的问题。
内容的提问来源于stack exchange,提问作者yaserso
相关产品推荐
相关产品推荐

