11import { describe , expectTypeOf , it } from 'vitest'
2+ import { z } from 'zod'
23import { createCollection } from '@tanstack/db'
34import { persistedCollectionOptions } from '../src'
45import type { PersistedCollectionUtils , PersistenceAdapter } from '../src'
5- import type { SyncConfig , UtilsRecord } from '@tanstack/db'
6+ import type { SyncConfig , UtilsRecord , WithVirtualProps } from '@tanstack/db'
7+
8+ type OutputWithVirtual <
9+ T extends object ,
10+ TKey extends string | number = string | number ,
11+ > = WithVirtualProps < T , TKey >
12+
13+ type ItemOf < T > = T extends Array < infer U > ? U : T
614
715type Todo = {
816 id : string
@@ -90,6 +98,11 @@ describe(`persisted collection types`, () => {
9098 // @ts -expect-error persistedCollectionOptions requires a persistence config
9199 persistedCollectionOptions ( {
92100 getKey : ( item : Todo ) => item . id ,
101+ } )
102+
103+ persistedCollectionOptions ( {
104+ getKey : ( item : Todo ) => item . id ,
105+ // @ts -expect-error persistedCollectionOptions requires a persistence config when sync is provided
93106 sync : {
94107 sync : ( { markReady } : { markReady : ( ) => void } ) => {
95108 markReady ( )
@@ -108,4 +121,182 @@ describe(`persisted collection types`, () => {
108121 } ,
109122 } )
110123 } )
124+
125+ it ( `should work with schema and infer correct types when saved to a variable in sync-absent mode` , ( ) => {
126+ const testSchema = z . object ( {
127+ id : z . string ( ) ,
128+ title : z . string ( ) ,
129+ createdAt : z . date ( ) . optional ( ) . default ( new Date ( ) ) ,
130+ } )
131+
132+ type ExpectedType = z . infer < typeof testSchema >
133+ type ExpectedInput = z . input < typeof testSchema >
134+
135+ const schemaAdapter : PersistenceAdapter < ExpectedType , string > = {
136+ loadSubset : ( ) => Promise . resolve ( [ ] ) ,
137+ applyCommittedTx : ( ) => Promise . resolve ( ) ,
138+ ensureIndex : ( ) => Promise . resolve ( ) ,
139+ }
140+
141+ const options = persistedCollectionOptions ( {
142+ id : `test-local-schema` ,
143+ schema : testSchema ,
144+ schemaVersion : 1 ,
145+ getKey : ( item ) => item . id ,
146+ persistence : { adapter : schemaAdapter } ,
147+ } )
148+
149+ expectTypeOf ( options . schema ) . toEqualTypeOf < typeof testSchema > ( )
150+
151+ const collection = createCollection ( options )
152+
153+ // Test that the collection has the correct inferred type from schema
154+ expectTypeOf ( collection . toArray ) . toEqualTypeOf <
155+ Array < OutputWithVirtual < ExpectedType , string > >
156+ > ( )
157+
158+ // Test insert parameter type
159+ type InsertParam = Parameters < typeof collection . insert > [ 0 ]
160+ expectTypeOf < ItemOf < InsertParam > > ( ) . toEqualTypeOf < ExpectedInput > ( )
161+
162+ // Check that the update method accepts the expected input type
163+ collection . update ( `1` , ( draft ) => {
164+ expectTypeOf ( draft ) . toEqualTypeOf < ExpectedInput > ( )
165+ } )
166+ } )
167+
168+ it ( `should work with schema and infer correct types when nested in createCollection in sync-absent mode` , ( ) => {
169+ const testSchema = z . object ( {
170+ id : z . string ( ) ,
171+ title : z . string ( ) ,
172+ createdAt : z . date ( ) . optional ( ) . default ( new Date ( ) ) ,
173+ } )
174+
175+ type ExpectedType = z . infer < typeof testSchema >
176+ type ExpectedInput = z . input < typeof testSchema >
177+
178+ const schemaAdapter : PersistenceAdapter < ExpectedType , string > = {
179+ loadSubset : ( ) => Promise . resolve ( [ ] ) ,
180+ applyCommittedTx : ( ) => Promise . resolve ( ) ,
181+ ensureIndex : ( ) => Promise . resolve ( ) ,
182+ }
183+
184+ const collection = createCollection (
185+ persistedCollectionOptions ( {
186+ id : `test-local-schema-nested` ,
187+ schema : testSchema ,
188+ schemaVersion : 1 ,
189+ getKey : ( item ) => item . id ,
190+ persistence : { adapter : schemaAdapter } ,
191+ } ) ,
192+ )
193+
194+ // Test that the collection has the correct inferred type from schema
195+ expectTypeOf ( collection . toArray ) . toEqualTypeOf <
196+ Array < OutputWithVirtual < ExpectedType , string > >
197+ > ( )
198+
199+ // Test insert parameter type
200+ type InsertParam = Parameters < typeof collection . insert > [ 0 ]
201+ expectTypeOf < ItemOf < InsertParam > > ( ) . toEqualTypeOf < ExpectedInput > ( )
202+
203+ // Check that the update method accepts the expected input type
204+ collection . update ( `1` , ( draft ) => {
205+ expectTypeOf ( draft ) . toEqualTypeOf < ExpectedInput > ( )
206+ } )
207+ } )
208+
209+ it ( `should work with schema and infer correct types when saved to a variable in sync-present mode` , ( ) => {
210+ const testSchema = z . object ( {
211+ id : z . string ( ) ,
212+ title : z . string ( ) ,
213+ createdAt : z . date ( ) . optional ( ) . default ( new Date ( ) ) ,
214+ } )
215+
216+ type ExpectedType = z . infer < typeof testSchema >
217+ type ExpectedInput = z . input < typeof testSchema >
218+
219+ const schemaAdapter : PersistenceAdapter < ExpectedType , string > = {
220+ loadSubset : ( ) => Promise . resolve ( [ ] ) ,
221+ applyCommittedTx : ( ) => Promise . resolve ( ) ,
222+ ensureIndex : ( ) => Promise . resolve ( ) ,
223+ }
224+
225+ const options = persistedCollectionOptions ( {
226+ id : `test-sync-schema` ,
227+ schema : testSchema ,
228+ schemaVersion : 1 ,
229+ getKey : ( item ) => item . id ,
230+ sync : {
231+ sync : ( { markReady } ) => {
232+ markReady ( )
233+ } ,
234+ } ,
235+ persistence : { adapter : schemaAdapter } ,
236+ } )
237+
238+ expectTypeOf ( options . schema ) . toEqualTypeOf < typeof testSchema > ( )
239+
240+ const collection = createCollection ( options )
241+
242+ // Test that the collection has the correct inferred type from schema
243+ expectTypeOf ( collection . toArray ) . toEqualTypeOf <
244+ Array < OutputWithVirtual < ExpectedType , string > >
245+ > ( )
246+
247+ // Test insert parameter type
248+ type InsertParam = Parameters < typeof collection . insert > [ 0 ]
249+ expectTypeOf < ItemOf < InsertParam > > ( ) . toEqualTypeOf < ExpectedInput > ( )
250+
251+ // Check that the update method accepts the expected input type
252+ collection . update ( `1` , ( draft ) => {
253+ expectTypeOf ( draft ) . toEqualTypeOf < ExpectedInput > ( )
254+ } )
255+ } )
256+
257+ it ( `should work with schema and infer correct types when nested in createCollection in sync-present mode` , ( ) => {
258+ const testSchema = z . object ( {
259+ id : z . string ( ) ,
260+ title : z . string ( ) ,
261+ createdAt : z . date ( ) . optional ( ) . default ( new Date ( ) ) ,
262+ } )
263+
264+ type ExpectedType = z . infer < typeof testSchema >
265+ type ExpectedInput = z . input < typeof testSchema >
266+
267+ const schemaAdapter : PersistenceAdapter < ExpectedType , string > = {
268+ loadSubset : ( ) => Promise . resolve ( [ ] ) ,
269+ applyCommittedTx : ( ) => Promise . resolve ( ) ,
270+ ensureIndex : ( ) => Promise . resolve ( ) ,
271+ }
272+
273+ const collection = createCollection (
274+ persistedCollectionOptions ( {
275+ id : `test-sync-schema-nested` ,
276+ schema : testSchema ,
277+ schemaVersion : 1 ,
278+ getKey : ( item ) => item . id ,
279+ sync : {
280+ sync : ( { markReady } ) => {
281+ markReady ( )
282+ } ,
283+ } ,
284+ persistence : { adapter : schemaAdapter } ,
285+ } ) ,
286+ )
287+
288+ // Test that the collection has the correct inferred type from schema
289+ expectTypeOf ( collection . toArray ) . toEqualTypeOf <
290+ Array < OutputWithVirtual < ExpectedType , string > >
291+ > ( )
292+
293+ // Test insert parameter type
294+ type InsertParam = Parameters < typeof collection . insert > [ 0 ]
295+ expectTypeOf < ItemOf < InsertParam > > ( ) . toEqualTypeOf < ExpectedInput > ( )
296+
297+ // Check that the update method accepts the expected input type
298+ collection . update ( `1` , ( draft ) => {
299+ expectTypeOf ( draft ) . toEqualTypeOf < ExpectedInput > ( )
300+ } )
301+ } )
111302} )
0 commit comments