From 1490651ed90caba05022675af76f769096f80516 Mon Sep 17 00:00:00 2001 From: trinadhkoya Date: Sun, 30 Aug 2026 22:05:05 +0530 Subject: [PATCH] fix(types): preserve argument signature of observable function children Function children that returned a non-void value were reduced to `() => t` in `ObservableFunctionChildren`, dropping their parameters, so calling such a function failed to type-check with "Expected 0 arguments, but got 1". The same reduction unwrapped the return type of async function children with no arguments, turning `Promise` into `T`. Use the original function type `T[K]` so the full call signature and return type are preserved. Fix proposed by the reporter in #631. Closes #631 Closes #521 --- src/observableTypes.ts | 2 +- tests/types.test.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/observableTypes.ts b/src/observableTypes.ts index fe6c81718..70f4999ac 100644 --- a/src/observableTypes.ts +++ b/src/observableTypes.ts @@ -150,7 +150,7 @@ type ObservableFunctionChildren = { ? T[K] : t extends Observable ? t - : Observable & (() => t) + : Observable & T[K] : T[K] & Observable; }; diff --git a/tests/types.test.ts b/tests/types.test.ts index 3ac5c4ffb..572de67bf 100644 --- a/tests/types.test.ts +++ b/tests/types.test.ts @@ -131,6 +131,46 @@ describe('observable', () => { type ObservableFn2 = ReturnType; expectTypeOf().returns.toEqualTypeOf(); }); + + it('issue #631: function child preserves argument signature', () => { + type AppState = { + // A function returning a non-void value previously had its argument signature + // stripped down to `() => boolean`, so calling it errored with + // `Expected 0 arguments, but got 1`. + centerMap: (coords: unknown) => boolean; + }; + + function withFunctionChild() { + return observable({ + centerMap: (coords) => !!coords, + }); + } + + type ObservableFn = ReturnType; + + // The function's argument signature must be preserved. + expectTypeOf().toBeCallableWith('some-coords'); + expectTypeOf().parameters.toEqualTypeOf<[coords: unknown]>(); + }); + + it('issue #521: async function child without args keeps its Promise return type', () => { + type Example = { + // Without arguments the Promise return was wrongly unwrapped to `number`. + asyncNoArgs(): Promise; + }; + + function withAsyncChildren() { + return observable({ + async asyncNoArgs() { + return 1; + }, + }); + } + + type ObservableFn = ReturnType; + + expectTypeOf().returns.toEqualTypeOf>(); + }); }); describe('Observable', () => {