From 259b79a1283807b1f8f0440256612f81c3ecfd2e Mon Sep 17 00:00:00 2001 From: Dmitrii Troitskii Date: Sat, 14 Mar 2026 02:33:57 +0000 Subject: [PATCH 1/2] fix(db): treat objects with Symbol.toStringTag as leaf values in IsPlainObject Temporal types (Temporal.PlainDate, ZonedDateTime, etc.) have Symbol.toStringTag set but are not in the JsBuiltIns union, causing IsPlainObject to return true and Ref to recursively walk their methods and mangle them to {}. Adds a Symbol.toStringTag check so all class instances (including all Temporal types) are treated as leaf values, preserving their types in query select projections. Fixes #1372 --- .changeset/fix-temporal-isplainobject.md | 11 +++++++++++ packages/db/src/query/builder/types.ts | 10 +++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-temporal-isplainobject.md diff --git a/.changeset/fix-temporal-isplainobject.md b/.changeset/fix-temporal-isplainobject.md new file mode 100644 index 000000000..10b5c424a --- /dev/null +++ b/.changeset/fix-temporal-isplainobject.md @@ -0,0 +1,11 @@ +--- +"@tanstack/db": patch +--- + +fix(db): treat objects with `Symbol.toStringTag` as leaf values in `IsPlainObject` + +Temporal types (e.g. `Temporal.PlainDate`, `Temporal.ZonedDateTime`) have `Symbol.toStringTag` set to a string. Previously, `IsPlainObject` would return `true` for these types because they are objects and not in the `JsBuiltIns` union. This caused the `Ref` mapped type to recursively walk Temporal methods, mangling them to `{}`. + +The fix adds a `T extends { readonly [Symbol.toStringTag]: string }` check before returning `true`, causing all class instances with `Symbol.toStringTag` (Temporal types, etc.) to be treated as leaf values with their types fully preserved. + +Fixes #1372 diff --git a/packages/db/src/query/builder/types.ts b/packages/db/src/query/builder/types.ts index 6dce531f8..476438c13 100644 --- a/packages/db/src/query/builder/types.ts +++ b/packages/db/src/query/builder/types.ts @@ -862,6 +862,12 @@ export type Prettify = { /** * IsPlainObject - Utility type to check if T is a plain object + * + * Returns `false` for: + * - Arrays (ReadonlyArray) + * - JavaScript built-ins (Date, Map, Set, etc.) + * - Objects with `Symbol.toStringTag` (class instances like Temporal types, + * TypedArrays not already in JsBuiltIns, etc.) — these are not plain data objects */ type IsPlainObject = T extends unknown ? T extends object @@ -869,7 +875,9 @@ type IsPlainObject = T extends unknown ? false : T extends JsBuiltIns ? false - : true + : T extends { readonly [Symbol.toStringTag]: string } + ? false + : true : false : false From 634beb3e31af28a6ebd1a9f24bdf3f0420729f55 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2026 02:35:10 +0000 Subject: [PATCH 2/2] ci: apply automated fixes --- .changeset/fix-temporal-isplainobject.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fix-temporal-isplainobject.md b/.changeset/fix-temporal-isplainobject.md index 10b5c424a..19f2f12f6 100644 --- a/.changeset/fix-temporal-isplainobject.md +++ b/.changeset/fix-temporal-isplainobject.md @@ -1,5 +1,5 @@ --- -"@tanstack/db": patch +'@tanstack/db': patch --- fix(db): treat objects with `Symbol.toStringTag` as leaf values in `IsPlainObject`