Skip to content

fix: normalize schema generic in PersistedSyncOptionsResult#1415

Open
ghardin1314 wants to merge 1 commit intoTanStack:mainfrom
ghardin1314:fix/persisted-collection-schema-generic
Open

fix: normalize schema generic in PersistedSyncOptionsResult#1415
ghardin1314 wants to merge 1 commit intoTanStack:mainfrom
ghardin1314:fix/persisted-collection-schema-generic

Conversation

@ghardin1314
Copy link
Copy Markdown

@ghardin1314 ghardin1314 commented Mar 26, 2026

Sorry, my clanker got a little over zealous in actually submitting this PR. This is a real issue when pairing queryCollectionOptions and persistedCollectionOptions. This fix did work for me when patched locally.

Also, there seems to be no contribution docs for this repo? I would like to keep contributing as I find things in my prototype but dont want to be burdensom

Summary

persistedCollectionOptions() wrapping queryCollectionOptions() produces a PersistedSyncOptionsResult whose TSchema generic doesn't match any createCollection overload, causing a type error when composing persisted + query-synced collections.

Repro — both of these fail:

// With schema
const queryOpts = queryCollectionOptions({ schema: todoSchema, ... })
createCollection(persistedCollectionOptions({ ...queryOpts, persistence, schemaVersion: 1 }))
// Error: schema?: ZodObject | undefined is not assignable to undefined

// Without schema
const queryOpts = queryCollectionOptions({ ... })
createCollection(persistedCollectionOptions({ ...queryOpts, persistence, schemaVersion: 1 }))
// Error: StandardSchemaV1<unknown, unknown> is not assignable to undefined

// Current Workaround
const queryOpts = queryCollectionOptions({
  ...,
  schema: todoSchema // Pass schema here
});

// 5. Compose: query sync wrapped in persisted collection
const collection = createCollection({
  ...persistedCollectionOptions({
    ...queryOpts,
    persistence,
    schemaVersion: 1,
  }),
  schema: todoSchema // pass again here after spreading persisted opts
});

Root cause: PersistedSyncOptionsResult passes TSchema through to CollectionConfig as-is. But createCollection has two overload groups:

  • "with schema": requires { schema: T } (required) + CollectionConfig<..., T, ...> where T extends StandardSchemaV1
  • "without schema": requires { schema?: never } + CollectionConfig<..., never, ...>

When TSchema is a concrete schema (ZodObject), the result has schema?: TSchema | undefined (optional from CollectionConfig) — matching neither overload. When TSchema is the default StandardSchemaV1<unknown, unknown>, it also fails the never check.

Fix: Add a NormalizeSchema<TSchema> helper that collapses both never and the default StandardSchemaV1<unknown, unknown> to never, then use it in both the CollectionConfig generic parameter and a conditional intersection that makes schema required or absent.

Test plan

  • persistedCollectionOptions({ ...queryCollectionOptions({ schema }), persistence }) passed to createCollection compiles
  • persistedCollectionOptions({ ...queryCollectionOptions({ /* no schema */ }), persistence }) passed to createCollection compiles
  • persistedCollectionOptions({ /* local-only, no sync */ }) still compiles
  • Existing tests pass

…eCollection compatibility

`persistedCollectionOptions()` wrapping `queryCollectionOptions()` produced a
`PersistedSyncOptionsResult` whose `TSchema` generic didn't match any
`createCollection` overload:

- With a concrete schema (e.g. ZodObject): `schema?: TSchema | undefined`
  matched neither the "with schema" overload (`{ schema: T }`) nor the
  "without schema" overload (`{ schema?: never }`).
- Without a schema: `TSchema` defaulted to `StandardSchemaV1<unknown, unknown>`
  which also failed the `never` check in `createCollection`.

Add `NormalizeSchema<TSchema>` helper that collapses both `never` and the
default `StandardSchemaV1<unknown, unknown>` to `never`, then use it in
both the `CollectionConfig` generic parameter and an intersection that
makes `schema` required (concrete) or `schema?: never` (absent).
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.

1 participant