题目描述
给函数PromiseAll指定类型,它接受元素为 Promise 或者类似 Promise 的对象的数组,返回值应为Promise<T>,其中T是这些 Promise 的结果组成的数组。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// 应推导出 `Promise<[number, 42, string]>`
const p = PromiseAll([promise1, promise2, promise3] as const)
题解
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
const promiseAllTest1 = PromiseAll([1, 2, 3] as const)
const promiseAllTest2 = PromiseAll([1, 2, Promise.resolve(3)] as const)
const promiseAllTest3 = PromiseAll([1, 2, Promise.resolve(3)])
const promiseAllTest4 = PromiseAll<Array<number | Promise<number>>>([1, 2, 3])
type cases = [
Expect<Equal<typeof promiseAllTest1, Promise<[1, 2, 3]>>>,
Expect<Equal<typeof promiseAllTest2, Promise<[1, 2, number]>>>,
Expect<Equal<typeof promiseAllTest3, Promise<[number, number, number]>>>,
Expect<Equal<typeof promiseAllTest4, Promise<number[]>>>,
]
// ============= Your Code Here =============
type MyAwaited<T> = T extends PromiseLike<infer R> ? R : T;
declare function PromiseAll(values: any): any
代码实现
下面是实现PromiseAll类型的过程:
初始代码
declare function PromiseAll(values: any): any
这是初始的PromiseAll类型定义,它接受一个 values,并返回any类型
引入泛型
首先引入泛型T,并用T extends unknown[]将其约束为数组类型
使用readonly [...T]作为 values 的类型,确保 values 是一个只读的元组类型
这样做的目的是,当传入一个元组时,TypeScript 会保留元组元素的精确类型,而不是把它们推断为一个更宽泛的联合类型。例如,如果你传入[1, 2, 3] as const,TypeScript 会将其视为[1, 2, 3]而不是number[]
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): any
使用映射类型创建元组
使用映射类型{ [K in keyof T]: T[K] }创建元组
[K in keyof T]遍历T的索引,T[K]表示对应索引元素的类型
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]):
Promise<{ [K in keyof T]: T[K] }>
到这里promiseAllTest1测试通过了,接着查看promiseAllTest2它推断出来的类型是Promise<[1, 2, Promise<number>]>,而我们的期望是Promise<[1, 2, number]>
对于Promise类型的元素,我们需要它的返回值的类型解析出来
解析 Promise 类型
还记得 189 - Awaited 吗?使用MyAwaited解析出嵌套的 Promise 的返回类型
type MyAwaited<T> = T extends PromiseLike<infer R> ? R : T
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]):
Promise<{ [K in keyof T]: MyAwaited<T[K]> }>
对于结果元组的每个元素T[K],使用MyAwaited<T[K]>来解析Promise元素。如果元素是Promise或PromiseLike对象,我们会得到它的返回值的类型,而不是Promise本身
分布式条件类型解释
为什么不直接在PromiseAll函数中使用内联推断元素的类型,而是单独使用MyAwaited呢?
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]):
Promise<{
[K in keyof T]: T[K] extends PromiseLike<infer R>
? R
: T[K]
}>;
解释:
这里涉及到分布式条件类型的问题。当T是联合类型时,如number | Promise<number>,使用内联推断会导致问题
内联推断的结果会是:
number | Promise<number> extends PromiseLike<infer R> ? R : number | Promise<number>
对于这种联合类型,内联推断会将整个联合类型作为一个整体进行判断,而不是分别处理联合类型中的每个成员。所以,它无法正确地将Promise<number>解析为number
而使用MyAwaited<number | Promise<number>>会分别处理联合类型的每个部分:
- •
MyAwaited<number>会返回number - •
MyAwaited<Promise<number>>会返回number(通过PromiseLike<nfer R>推断出R为number)
这样,最终结果会是number,这正是我们想要的结果