Skip to content

fix(db): treat objects with Symbol.toStringTag as leaf values in IsPlainObject#1373

Open
sleitor wants to merge 2 commits intoTanStack:mainfrom
sleitor:fix-1372
Open

fix(db): treat objects with Symbol.toStringTag as leaf values in IsPlainObject#1373
sleitor wants to merge 2 commits intoTanStack:mainfrom
sleitor:fix-1372

Conversation

@sleitor
Copy link
Contributor

@sleitor sleitor commented Mar 14, 2026

Summary

Fixes #1372

Temporal types (e.g. Temporal.PlainDate, Temporal.ZonedDateTime, Temporal.Instant) 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<T> mapped type to recursively walk their methods and mangle them to {}.

Root Cause

In packages/db/src/query/builder/types.ts, IsPlainObject<T> determines how each field is processed through the Ref<T> mapped type:

  • Plain objects → recursively walked to build nested refs
  • Non-plain objects (arrays, JsBuiltIns like Date, Map, etc.) → treated as leaf values, type preserved as-is

Temporal.PlainDate extends object but is not in the JsBuiltIns union, so IsPlainObject returns true. This causes Ref<T> to recursively walk its methods, producing:

Type '{ readonly [Symbol.toStringTag]: "Temporal.PlainDate"; equals: {}; ... }' 
  is not assignable to type 'PlainDate'

Fix

Adds a T extends { readonly [Symbol.toStringTag]: string } check to IsPlainObject before returning true. Objects with Symbol.toStringTag are class instances (Temporal types, custom tagged objects), not plain data objects. This covers all Temporal types without requiring an explicit import or a maintained hardcoded list.

type IsPlainObject<T> = T extends unknown
  ? T extends object
    ? T extends ReadonlyArray<any>
      ? false
      : T extends JsBuiltIns
        ? false
        : T extends { readonly [Symbol.toStringTag]: string }
          ? false  // ← new: class instances with toStringTag are leaf values
          : true
    : false
  : false

Testing

Lint and build pass. The type change is entirely in types.ts with no runtime behavior affected.

…ainObject

Temporal types (Temporal.PlainDate, ZonedDateTime, etc.) have
Symbol.toStringTag set but are not in the JsBuiltIns union, causing
IsPlainObject to return true and Ref<T> 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 TanStack#1372
@changeset-bot
Copy link

changeset-bot bot commented Mar 14, 2026

🦋 Changeset detected

Latest commit: 634beb3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@tanstack/db Patch
@tanstack/angular-db Patch
@tanstack/electric-db-collection Patch
@tanstack/offline-transactions Patch
@tanstack/powersync-db-collection Patch
@tanstack/query-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 14, 2026

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@1373

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@1373

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@1373

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@1373

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@1373

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@1373

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@1373

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@1373

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@1373

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@1373

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@1373

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@1373

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@1373

commit: e29f108

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Temporal types lose type information in query select projections

1 participant