TypeScript元组下Promise.all顺序执行函数的类型适配问题
实现顺序执行的Promise.all替代函数(带TypeScript类型支持)
我想实现一个类似Promise.all的函数,但它不会并行执行所有Promise,而是接收一个回调函数数组并按顺序执行。以下是我写的JavaScript实现:
function promiseAllSequential(promises) { return promises.reduce( (promiseAccumulator, currentPromise) => promiseAccumulator.then((resultAccumulator) => currentPromise.then((currentResult) => [...resultAccumulator, currentResult]) ), Promise.resolve([]) ); }
Promise.all的TypeScript类型签名如下:
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;
为了让我的函数行为和它对齐,我设计了如下类型签名:
promiseAllSequential<FunctionsTuple extends readonly (() => unknown)[] | []>( functions: FunctionsTuple ): Promise<{ -readonly [TupleIndex in keyof FunctionsTuple]: Awaited<ReturnType<FunctionsTuple[TupleIndex]>>; }>
但我没法让这个类型和代码适配,核心问题是处理元组时如何给reduce函数添加正确的类型。之前我尝试用大量类型断言来解决错误,但想知道有没有不用断言的TypeScript特性可以实现这个需求。
解决方案:用递归类型推导替代reduce(或精简断言)
方案1:递归实现(类型推导更自然)
递归方式能让TypeScript自然跟踪元组每一步的类型,无需额外断言:
type SequentialResult<Fns extends readonly (() => unknown)[]> = { [K in keyof Fns]: Awaited<ReturnType<Fns[K]>>; }; function promiseAllSequential<Fns extends readonly (() => unknown)[]>( functions: Fns ): Promise<SequentialResult<Fns>> { async function execute(index: number, results: unknown[]): Promise<SequentialResult<Fns>> { if (index >= functions.length) { return results as SequentialResult<Fns>; } const currentResult = await functions[index](); results.push(currentResult); return execute(index + 1, results); } return execute(0, []); }
方案2:给reduce显式指定类型参数
如果坚持使用reduce,可以手动指定reduce的类型参数,让TypeScript识别累积器是对应元组的Promise,仅保留一次必要断言:
function promiseAllSequential<Fns extends readonly (() => unknown)[]>( functions: Fns ): Promise<{ [K in keyof Fns]: Awaited<ReturnType<Fns[K]>> }> { type ResultTuple = { [K in keyof Fns]: Awaited<ReturnType<Fns[K]>> }; return functions.reduce( (acc, fn) => acc.then(async (results) => [...results, await fn()] as Partial<ResultTuple>), Promise.resolve([]) as Promise<Partial<ResultTuple>> ) as Promise<ResultTuple>; }
这里仅在最后一步将Partial<ResultTuple>转为完整元组类型,因为我们能确定执行完所有函数后数组会填满所有元组项,相比大量临时断言更简洁。
内容的提问来源于stack exchange,提问作者user9540018
相关产品推荐
相关产品推荐

