diff --git a/.changeset/svelte-query-no-infer-tdata.md b/.changeset/svelte-query-no-infer-tdata.md new file mode 100644 index 00000000000..338f8fed392 --- /dev/null +++ b/.changeset/svelte-query-no-infer-tdata.md @@ -0,0 +1,5 @@ +--- +'@tanstack/svelte-query': patch +--- + +fix(svelte-query): wrap `TData` in `NoInfer` on `createQuery` and `createInfiniteQuery` return types so `TData` is inferred from the input options only (matching `react-query`, `preact-query`, and `vue-query`). Prevents the result-type annotation from silently widening `TData` and improves `select` inference (#7673). diff --git a/packages/svelte-query/src/createInfiniteQuery.ts b/packages/svelte-query/src/createInfiniteQuery.ts index e8fe7948909..f909ccc3c74 100644 --- a/packages/svelte-query/src/createInfiniteQuery.ts +++ b/packages/svelte-query/src/createInfiniteQuery.ts @@ -3,6 +3,7 @@ import { createBaseQuery } from './createBaseQuery.svelte.js' import type { DefaultError, InfiniteData, + NoInfer, QueryClient, QueryKey, QueryObserver, @@ -30,7 +31,7 @@ export function createInfiniteQuery< > >, queryClient?: Accessor, -): CreateInfiniteQueryResult { +): CreateInfiniteQueryResult, TError> { return createBaseQuery( options, InfiniteQueryObserver as typeof QueryObserver, diff --git a/packages/svelte-query/src/createQuery.ts b/packages/svelte-query/src/createQuery.ts index bf7efe81a74..cfe2f7dc14b 100644 --- a/packages/svelte-query/src/createQuery.ts +++ b/packages/svelte-query/src/createQuery.ts @@ -1,6 +1,11 @@ import { QueryObserver } from '@tanstack/query-core' import { createBaseQuery } from './createBaseQuery.svelte.js' -import type { DefaultError, QueryClient, QueryKey } from '@tanstack/query-core' +import type { + DefaultError, + NoInfer, + QueryClient, + QueryKey, +} from '@tanstack/query-core' import type { Accessor, CreateQueryOptions, @@ -22,7 +27,7 @@ export function createQuery< UndefinedInitialDataOptions >, queryClient?: Accessor, -): CreateQueryResult +): CreateQueryResult, TError> export function createQuery< TQueryFnData = unknown, @@ -34,7 +39,7 @@ export function createQuery< DefinedInitialDataOptions >, queryClient?: Accessor, -): DefinedCreateQueryResult +): DefinedCreateQueryResult, TError> export function createQuery< TQueryFnData, @@ -44,7 +49,7 @@ export function createQuery< >( options: Accessor>, queryClient?: Accessor, -): CreateQueryResult +): CreateQueryResult, TError> export function createQuery( options: Accessor, diff --git a/packages/svelte-query/tests/createQuery/createQuery.test-d.ts b/packages/svelte-query/tests/createQuery/createQuery.test-d.ts index 8b6590b3347..1b7b1398be8 100644 --- a/packages/svelte-query/tests/createQuery/createQuery.test-d.ts +++ b/packages/svelte-query/tests/createQuery/createQuery.test-d.ts @@ -1,8 +1,47 @@ import { describe, expectTypeOf, it } from 'vitest' import { queryKey } from '@tanstack/query-test-utils' import { createQuery, queryOptions } from '../../src/index.js' +import type { CreateQueryResult } from '../../src/index.js' describe('createQuery', () => { + describe('select', () => { + it('should infer select data type from queryFn return (issue #7673)', () => { + const key = queryKey() + const { data } = createQuery(() => ({ + queryKey: key, + queryFn: () => Promise.resolve({ a: { b: { c: 'hello' } } }), + select: (input) => input.a.b.c, + })) + + expectTypeOf(data).toEqualTypeOf() + }) + + it('should infer select data type when options come from queryOptions', () => { + const key = queryKey() + const options = queryOptions({ + queryKey: key, + queryFn: () => Promise.resolve(1), + }) + const { data } = createQuery(() => ({ + ...options, + select: (input) => input > 1, + })) + + expectTypeOf(data).toEqualTypeOf() + }) + + // eslint-disable-next-line vitest/expect-expect + it('TData should depend on arguments only, not on the result type annotation', () => { + // @ts-expect-error TData inferred from queryFn ({ wow: boolean }), not from result + const result: CreateQueryResult<{ wow: string }> = createQuery(() => ({ + queryKey: queryKey(), + queryFn: () => ({ wow: true }), + })) + + void result + }) + }) + describe('initialData', () => { describe('Config object overload', () => { it('TData should always be defined when initialData is provided as an object', () => {