type ResolveObject = T extends object ? T extends Promise ? ResolveObject : { [k in keyof T]: ResolveObject } : T; /** Recursively resolves deep objects with nested promises. */ export async function promiseObject(obj: T): Promise> { const seen = new Map(); async function resolver(obj: T): Promise> { // null / undefined if (obj == null) return obj as ResolveObject; // other primitive if (typeof obj !== "object") return obj as ResolveObject; if (seen.has(obj)) return seen.get(obj) as ResolveObject; // PromiseLike if ("then" in obj && typeof obj.then === "function") { const resolved = resolver(await obj); seen.set(obj, resolved); return resolved as ResolveObject; } // Array if (Array.isArray(obj)) { const resolved = await Promise.all(obj.map(each => Promise.resolve(each).then(resolver))); seen.set(obj, resolved); return resolved as ResolveObject; } // object const ret = {} as ResolveObject; seen.set(obj, ret); // @ts-ignore for (const [key, value] of Object.entries(obj)) ret[key] = await resolver(value); seen.set(obj, ret); return ret; } return resolver(obj); } export default promiseObject;