我正在尝试编写一个函数,该函数接受
PromiseSettledResult
数组(Promise.allSettled 返回的 类型)并返回已完成值或拒绝原因的数组:
function unwrapResults<T>(results: ReadonlyArray<PromiseSettledResult<T>>): Array<T> {
return results.map((result) => result.status === "fulfilled" ? result.value : result.reason);
}
它在以下示例中工作:
const results = await Promise.allSettled([
Promise.resolve(1),
Promise.resolve("ok"),
Promise.reject(new Error("error"))
]);
const unwrappedResults = unwrapResults<number | string | Error>(results);
问题在于:
unwrapResults
类型。unwrappedResults
内的元素将是number | string | Error
类型。有没有办法改善这个问题?理想情况下,我希望函数调用中的手动输入更少,并且
unwrappedResults[0]
成为 number
,unwrappedResults[1]
成为 string
,unwrappedResults[2]
成为 Error
。
我要说的是,它不能按您期望的方式工作的原因是由于打字稿中关于如何检查泛型和联合类型的限制。具体来说,因为
PromiseSettledResult<string | number>
与 PromiseSettledResult<string> | PromiseSettledResult<number>
不同,联合体最终的结构略有不同,因为它们由打字稿表示,但 .value
字段最终都将是 string | number
因此,要使其正常工作,我们必须将泛型提升一个级别并表示
PromiseSettledResult
类型,然后使用条件类型从泛型中提取 .value
字段。 类似这样的事情:
type UnwrappedSettled<T extends PromiseSettledResult<any>> = T extends PromiseFulfilledResult<infer V> ? V : Error
function unwrapOneResult<T extends PromiseSettledResult<unknown>>(result: T): UnwrappedSettled<T>{
return result.status === "fulfilled" ? result.value : result.reason
}
async function test(){
const results = await Promise.allSettled([
Promise.resolve(1),
Promise.resolve("ok"),
Promise.reject(new Error("error"))
]);
const unwrapped = results.map(unwrapOneResult)
// ^? const unwrapped: (string | number | Error)[]
}
那么写你的
unwrapResults
是同样容易:
function unwrapResults<T extends PromiseSettledResult<any>>(results: T[]): Array<UnwrappedSettled<T>> {
return results.map(unwrapOneResult)
}
unwrapOneResult
可以在此版本中内联,它只是用于表明类型安全仍然按照您的预期工作,在内联版本中您会得到any
而不是Error
,所以它不会'严格进行类型检查。