From 1ade33491f2e6bdd37e645c4d39394e2654b53aa Mon Sep 17 00:00:00 2001 From: Antony Rizzitelli Date: Mon, 2 Nov 2026 20:58:04 +0100 Subject: [PATCH] fix(database): resolve a table update through its type to the modified count Reading the result of `update` through its type (`ReturnType`) took the last overload, whose conditional return resolved to `any`. The overloads are reordered so that the document overload, which returns `Query`, is the one a type-level read sees. Calls type exactly as before. --- src/staged-query/selection.ts | 24 ++++++++++++++++++------ src/tests/helpers/same-type.ts | 7 +++++++ src/tests/update_result.test.ts | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/tests/update_result.test.ts diff --git a/src/staged-query/selection.ts b/src/staged-query/selection.ts index 12887b7..b7b0981 100644 --- a/src/staged-query/selection.ts +++ b/src/staged-query/selection.ts @@ -32,15 +32,21 @@ function isUpdater( */ export class SingleSelection extends Datum { /** - * Update fields of this selection with the given values + * Update fields of this selection with values computed from each document * - * @param document Partial document with new values + * @param document Function building the partial document from the current one * @returns Number of modified documents */ - public update(document: DeepPartial & object): Query; public update( document: (val: ValueProxy) => U, ): ExtractType extends DeepPartial ? Query : never; + /** + * Update fields of this selection with the given values + * + * @param document Partial document with new values + * @returns Number of modified documents + */ + public update(document: DeepPartial & object): Query; public update( document: (DeepPartial & object) | ((val: ValueProxy) => U), ) { @@ -90,15 +96,21 @@ export class SingleSelection extends Datum { */ export class Selection extends Stream { /** - * Update fields of this selection with the given values + * Update fields of this selection with values computed from each document * - * @param document Partial document with new values + * @param document Function building the partial document from the current one * @returns Number of modified documents */ - public update(document: DeepPartial & object): Query; public update( document: (val: ValueProxy) => U, ): ExtractType extends DeepPartial ? Query : never; + /** + * Update fields of this selection with the given values + * + * @param document Partial document with new values + * @returns Number of modified documents + */ + public update(document: DeepPartial & object): Query; public update( document: (DeepPartial & object) | ((val: ValueProxy) => U), ) { diff --git a/src/tests/helpers/same-type.ts b/src/tests/helpers/same-type.ts index 695c559..5555e93 100644 --- a/src/tests/helpers/same-type.ts +++ b/src/tests/helpers/same-type.ts @@ -4,3 +4,10 @@ export type Same = [A] extends [B] ? true : false : false; + +/** Resolves to `true` when `A` is `any`, else to `false`. `Same` cannot tell, as `any` is assignable both ways. */ +type IsAny = 0 extends 1 & A ? true : false; + +/** Like `Same`, but `any` on either side resolves to `false`. */ +export type Exact = + IsAny extends true ? false : IsAny extends true ? false : Same; diff --git a/src/tests/update_result.test.ts b/src/tests/update_result.test.ts new file mode 100644 index 0000000..eb41098 --- /dev/null +++ b/src/tests/update_result.test.ts @@ -0,0 +1,33 @@ +import { expect } from "chai"; +import { Schema, type Query } from ".."; +import type { Exact } from "./helpers/same-type"; +import { Vehicle } from "./datasets/vehicles"; + +const schema = new Schema<{ vehicles: Vehicle }>("test-update-result", { + vehicles: Vehicle, +}); +const vehicles = schema.instance("default").table("vehicles"); + +describe("Update result", () => { + it("Resolves an update call to a query of the modified count", () => { + const viaDocument = vehicles.get("a").update({ price: 1 }); + const viaUpdater = vehicles.get("a").update(() => ({ price: 2 })); + const document: Exact> = true; + const updater: Exact> = true; + expect(document && updater).to.equal(true); + }); + + it("Resolves the update of a table, read through its type, to the modified count", () => { + const table: Exact< + Awaited["run"]>>, + number + > = true; + const single: Exact< + Awaited< + ReturnType["update"]>["run"]> + >, + number + > = true; + expect(table && single).to.equal(true); + }); +});