起因是做 type-challenges 上的一道类型题:
键入函数 PromiseAll
,它接受 PromiseLike 对象数组,返回值应为 Promise<T>
,其中 T
是解析的结果数组。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// expected to be `Promise<[number, 42, string]>`
const p = PromiseAll([promise1, promise2, promise3] as const)
你的代码:
declare function PromiseAll(values: any): any
测试用例:
import type { Equal, Expect } from '@type-challenges/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)])
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]>>>,
]
有兴趣可以自己做一下,答题链接
先说下正确答案:
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{
[key in keyof T]: Awaited<T[key]>
}>
这个不难想到,从测试用例可以看到 values
应是一个只读的元组,文档中有一段描述:
Tuples can also have rest elements, which have to be an array/tuple type.(元组中可以有剩余元素,但剩余元素的类型必须为数组/元组类型)
type StringNumberBooleans = [string, number, ...boolean[]];
type StringBooleansNumber = [string, ...boolean[], number];
type BooleansStringNumber = [...boolean[], string, number];
那么就可以通过 T extends any[]
和 readonly [...T]
来构造只读元组类型。然后遍历这个元组,再给 T[key]
套一个 Awatied<T>
解决 Promise
的问题。
问题解决了,完美啊。但又细细看了看测试用例,感觉不太对劲?(1)为什么第三个测试用例返回的是 [number, number, number]
而非 [1, 2, 3]
呢?
我又想试试别的方法,如果我让 values
是一个只读数组,map
的时候把 readonly
去掉不也行吗?
declare function PromiseAll<T extends readonly any[]>(values: T): Promise<{
-readonly [key in keyof T]: Awaited<T[key]>
}>
然而第三个用例又过不去了...
const promiseAllTest3 = PromiseAll([1, 2, Promise.resolve(3)]) // Promise<number[]>
这次直接变成 Promise<number[]>
了,(2)这又是怎么回事?