Во-первых, обратите внимание, что combinePromises
должен вернуть Обещание, поскольку Promise.all
возвращает Обещание.
function combinePromises(obj): Promise<...>
Теперь, что нужно добавить в ...
?Ну, вы хотите, чтобы результат был в основном тем же объектом, который был передан, но с разрешенными значениями.
Применение тех же ключей
Чтобы возвращаемое значение имело те же ключи, что ипереданное в значение, вам нужно использовать обобщенные значения и оператор keyof
:
type MyType<T> = { [K in keyof T]: ... };
function combinePromises<T>(obj: T): Promise<MyType<T>>
Теперь, после того как вы await
получите результат combinePromises
, вы получитеобъект, ключи которого совпадают с ключами переданного объекта.
Применение аналогичных значений
Вторая часть немного сложнее, поскольку вы не можете развернуть Promises для извлечения внутренних типов.Тем не менее, вы можете определить условный тип , который выводит тип того, что передано 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
then
они уже разрешены.
Полное решение будет выглядеть примерно так:
async function fetchUser(): Promise<User> {
return new User();
}
async function fetchCompany(): Promise<Company> {
return new Company();
}
class User { name:string }
class Company { }
type MyType<T> = { [K in keyof T]: ThenArg<T[K]> };
type ThenArg<T> = T extends Promise<infer U> ? U : any;
export const combinePromises = function combinePromises<T>(obj: T): Promise<MyType<T>> {
const keys = Object.keys(obj);
const values = Object.values(obj);
return Promise.all(values).then((results) => {
const combinedResult: MyType<T> = {} as MyType<T>;
results.forEach((result, i) => {
const key = keys[i];
combinedResult[key] = result;
});
return combinedResult;
});
};
(async () => {
const { user, company } = await combinePromises({
user: fetchUser(),
company: fetchCompany()
});
user.name; // we can access user.name since that's a member of User
})();