diff --git a/packages/inter-protocol/src/collect.js b/packages/inter-protocol/src/collect.js index 5408fb2242a..c05c4dc780f 100644 --- a/packages/inter-protocol/src/collect.js +++ b/packages/inter-protocol/src/collect.js @@ -3,7 +3,7 @@ const { fromEntries, keys, values } = Object; /** @type { (xs: X[], ys: Y[]) => [X, Y][]} */ export const zip = (xs, ys) => harden(xs.map((x, i) => [x, ys[+i]])); -/** @type { (obj: Record>) => Promise> } */ +/** @type { >>(obj: T) => Promise<{ [K in keyof T]: Awaited}> } */ export const allValues = async obj => { const resolved = await Promise.all(values(obj)); // @ts-expect-error cast diff --git a/packages/internal/src/utils.js b/packages/internal/src/utils.js index abd8d23e8ef..267e04f840c 100644 --- a/packages/internal/src/utils.js +++ b/packages/internal/src/utils.js @@ -248,18 +248,25 @@ export const bindAllMethods = obj => ); harden(bindAllMethods); +/** + * @template T + * @typedef {{[KeyType in keyof T]: T[KeyType]} & {}} Simplify + * flatten the type output to improve type hints shown in editors + * https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts + */ + +/** + * @typedef {(...args: any[]) => any} Callable + */ + /** * @template {{}} T - * @typedef {{ [K in keyof T]: DeeplyAwaited }} DeeplyAwaitedObject + * @typedef {{ [K in keyof T]: T[K] extends Callable ? T[K] : DeeplyAwaited }} DeeplyAwaitedObject */ /** - * Caveats: - * - doesn't recur within Promise results - * - resulting type has wrapper in its name - * * @template T - * @typedef {T extends PromiseLike ? Awaited : T extends {} ? DeeplyAwaitedObject : Awaited} DeeplyAwaited + * @typedef {T extends PromiseLike ? Awaited : T extends {} ? Simplify> : Awaited} DeeplyAwaited */ /** diff --git a/packages/internal/test/test-utils.js b/packages/internal/test/test-utils.js index 72167721e87..1a047079031 100644 --- a/packages/internal/test/test-utils.js +++ b/packages/internal/test/test-utils.js @@ -2,6 +2,7 @@ import test from 'ava'; import '@endo/init'; +import { Far } from '@endo/marshal'; import { fromUniqueEntries, objectMap, @@ -10,6 +11,7 @@ import { whileTrue, untilTrue, forever, + deeplyFulfilledObject, } from '../src/utils.js'; test('fromUniqueEntries', t => { @@ -57,6 +59,24 @@ test('objectMap', t => { ); }); +test('deeplyFulfilledObject', async t => { + const someFar = Far('somefar', { getAsync: () => Promise.resolve('async') }); + const unfulfilled = harden({ + obj1: { + obj2a: { + stringP: Promise.resolve('foo'), + }, + obj2b: someFar, + }, + }); + const fulfilled = await deeplyFulfilledObject(unfulfilled); + // JS check that it's a now string + fulfilled.obj1.obj2a.stringP.length; + t.deepEqual(fulfilled, { + obj1: { obj2a: { stringP: 'foo' }, obj2b: someFar }, + }); +}); + test('makeMeasureSeconds', async t => { const times = [1000.25, 2000.75, NaN]; /** @type {() => number} */