挑战
给函数 PromiseAll
指定类型,它接受元素为 Promise 或者类似 Promise 的对象的数组,返回值应为Promise<T>
,其中T
是这些 Promise 的结果组成的数组。
解答
这道题要实现一个类似 Promise.all()
的类型。
通过题目描述,我们知道这道题要通过 PromiseAll
的参数得到返回的类型,所以我们需要通过泛型将两个类型关联起来。
并且返回值为一个 Promise
数组,我们先返回一个 Promise<R>
。
R
需要我们操作 T
来生成一个新的数组,要得到 Promise 或者类似 Promise 的对象,可以使用 TypeScript 内置的 Awaited
获取。
这段代码看起来可能有些奇怪,因为返回的 Promise 看起来像是包裹了一个对象,不是我们期待的数组。但其实,这里的 T
我们知道是一个数组类型,所以 keyof T
实际上是数组的索引类型,也就是数字类型。
因此,这个映射类型实际上是一个元组类型,每个元素对应输入数组中的一个元素的解析值。
例如,如果 T
是 [Promise<number>, Promise<string>]
,那么 [key in keyof T]: Awaited<T[key]>;
就是 [number, string]
,相当于做了一个 map
,不会改变 T
是一个元组类型的事实。
实现到这里,会发现第三个 case 不通过,我们的实现返回了 Promise<number[]>
,但这道题期望我们返回 Promise<[number, number, number]
。通过观察发现第三个 case 没有加 as const
,所以 TypeScript 会把 T
推导成 (number | Promise<number>)[]
,然后通过 Awaited
之后,就只剩下了 number
。
所以我们需要完整地保留类型,这里需要用到一个特性叫做 变长元组类型(Variadic Tuple Types)。简单来说,变长元组类型通过 [...T]
的形式,将元组类型 T
展开成一个独立的元素列表。在处理变长参数时很有用,能够允许我们传递一个变长的参数列表,同时保留类型信息。
所以最终实现为: