I have this existing JS function, which is an alternative interface to Promise.all()
. Is there a way to type it with TS?
// Like Promise.all() but taking an object<key,promise<?>> and returning object<key,promiseValue>
export const combinePromises = obj => {
const keys = Object.keys(obj);
const values = Object.values(obj);
return Promise.all(values).then(results => {
const combinedResult = {};
results.forEach((result, i) => {
const key = keys[i];
combinedResult[key] = result;
});
return combinedResult;
});
};
I'd like to be able to do
const {user,company} = await combinePromises({
user: fetchUser(),
company: fetchCompany()
});
Given the fetch functions are typed, I expect the response (user and company) to be typed as well.
答案 0 :(得分:2)
首先,请注意combinePromises
必须返回一个Promise,因为Promise.all
返回一个Promise。
function combinePromises(obj): Promise<...>
现在,...
中需要做什么?好吧,您希望结果基本上与传入的对象相同,但是值已解析。
要强制返回的值与传入的值具有相同的键,您需要使用泛型和keyof
运算符:
type MyType<T> = { [K in keyof T]: ... };
function combinePromises<T>(obj: T): Promise<MyType<T>>
现在,在await
的结果combinePromises
之后,您将拥有一个对象,其键与传入对象的键相同。
第二部分比较棘手,因为您不能解开Promise来获取内部类型。但是,您可以定义一个conditional type来推断提供给Promise<...>
的内容的类型:
type ThenArg<T> = T extends Promise<infer U> ? U : any;
该定义意味着,如果可以将提供给T
的类型ThenArg<...>
分配给Promise<infer U>
,则返回类型U
。否则,默认输入any
。
因此ThenArg<Promise<User>>
将返回User
。
您的最终类型定义和函数签名应如下所示:
type MyType<T> = { [K in keyof T]: ThenArg<T[K]> }; // T[K] is the Promise value of the object passed in
type ThenArg<T> = T extends Promise<infer U> ? U : any;
function combinePromises<T>(obj: T): Promise<MyType<T>>
combinedResult
声明应类似于:
const combinedResult: MyType<T> = {} as MyType<T>;
同一类型的对象只是没有Promise层,因为在Promise.all
的{{1}}内部,它们已经被解析。
完整的解决方案如下所示:
then
答案 1 :(得分:0)
我刚刚做到了:
const [ user, company ] = await Promise.all([
fetchUser(),
fetchCompany()
]);
并找到正确的类型。
https://stackblitz.com/edit/typescript-combine-promise-typed
编辑:
这不是一个好的答案,因为它不能直接回答OP问题。但这可能对其他人有益,因为您知道您可以以这种方式使用Promise.all
。也许OP可以考虑直接使用Promise.all
。