From f48877eae3b4295495c609dc7096a0071e696814 Mon Sep 17 00:00:00 2001 From: Raman Paulau Date: Tue, 29 Apr 2025 13:51:04 -0700 Subject: [PATCH 1/2] fix(vue-query): more specific function overloads for queryOptions and useQuery to come before general ones (#9069) This instructs TypeScript to pick correct function overload when initialData is defined. --- .../src/__tests__/queryOptions.test-d.ts | 78 +++++++++++++++++++ .../src/__tests__/useQuery.test-d.ts | 10 +++ packages/vue-query/src/queryOptions.ts | 8 +- packages/vue-query/src/useQuery.ts | 14 ++-- 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/packages/vue-query/src/__tests__/queryOptions.test-d.ts b/packages/vue-query/src/__tests__/queryOptions.test-d.ts index 8ee93aadcab..d7a4c6ca8f6 100644 --- a/packages/vue-query/src/__tests__/queryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/queryOptions.test-d.ts @@ -122,4 +122,82 @@ describe('queryOptions', () => { expectTypeOf(data).toEqualTypeOf>() expectTypeOf(data2).toEqualTypeOf>() }) + + it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { + const { data } = reactive( + useQuery(queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => ({ + wow: true, + }), + })), + ) + + expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() + }) + + it('TData should have undefined in the union when initialData is NOT provided', () => { + const { data } = reactive( + useQuery(queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + })), + ) + + expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() + }) + + it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { + const { data } = reactive( + useQuery(queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => undefined as { wow: boolean } | undefined, + })), + ) + + expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() + }) + + it('TData should be narrowed after an isSuccess check when initialData is provided as a function which can return undefined', () => { + const { data, isSuccess } = reactive( + useQuery(queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => undefined as { wow: boolean } | undefined, + } + )), + ) + + if (isSuccess) { + expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() + } + }) + + it('data should not have undefined when initialData is provided', () => { + const { data } = reactive( + useQuery(queryOptions({ + queryKey: ['query-key'], + initialData: 42, + }))) + + expectTypeOf(data).toEqualTypeOf() + }) }) diff --git a/packages/vue-query/src/__tests__/useQuery.test-d.ts b/packages/vue-query/src/__tests__/useQuery.test-d.ts index a02ee4b638c..dead867a172 100644 --- a/packages/vue-query/src/__tests__/useQuery.test-d.ts +++ b/packages/vue-query/src/__tests__/useQuery.test-d.ts @@ -125,6 +125,16 @@ describe('useQuery', () => { expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() } }) + + it('data should not have undefined when initialData is provided', () => { + const { data } = reactive( + useQuery({ + queryKey: ['query-key'], + initialData: 42, + })) + + expectTypeOf(data).toEqualTypeOf() + }) }) describe('custom composable', () => { diff --git a/packages/vue-query/src/queryOptions.ts b/packages/vue-query/src/queryOptions.ts index 558756c44f7..4681080f8ca 100644 --- a/packages/vue-query/src/queryOptions.ts +++ b/packages/vue-query/src/queryOptions.ts @@ -10,8 +10,8 @@ export function queryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: UndefinedInitialQueryOptions, -): UndefinedInitialQueryOptions & { + options: DefinedInitialQueryOptions, +): DefinedInitialQueryOptions & { queryKey: DataTag } @@ -21,8 +21,8 @@ export function queryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: DefinedInitialQueryOptions, -): DefinedInitialQueryOptions & { + options: UndefinedInitialQueryOptions, +): UndefinedInitialQueryOptions & { queryKey: DataTag } diff --git a/packages/vue-query/src/useQuery.ts b/packages/vue-query/src/useQuery.ts index 9ac47bd9531..fd60dee2ee5 100644 --- a/packages/vue-query/src/useQuery.ts +++ b/packages/vue-query/src/useQuery.ts @@ -3,6 +3,7 @@ import { useBaseQuery } from './useBaseQuery' import type { DefaultError, DefinedQueryObserverResult, + InitialDataFunction, QueryKey, QueryObserverOptions, } from '@tanstack/query-core' @@ -64,7 +65,10 @@ export type UndefinedInitialQueryOptions< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = UseQueryOptions & { - initialData?: undefined | (() => undefined) + initialData?: + | undefined + | InitialDataFunction> + | NonUndefinedGuard } export type DefinedInitialQueryOptions< @@ -95,9 +99,9 @@ export function useQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: UndefinedInitialQueryOptions, + options: DefinedInitialQueryOptions, queryClient?: QueryClient, -): UseQueryReturnType +): UseQueryDefinedReturnType export function useQuery< TQueryFnData = unknown, @@ -105,9 +109,9 @@ export function useQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - options: DefinedInitialQueryOptions, + options: UndefinedInitialQueryOptions, queryClient?: QueryClient, -): UseQueryDefinedReturnType +): UseQueryReturnType export function useQuery< TQueryFnData = unknown, From 835d48c17f0031595abab21d2f273e077c404f09 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 21:49:47 +0000 Subject: [PATCH 2/2] ci: apply automated fixes --- .../src/__tests__/queryOptions.test-d.ts | 76 +++++++++++-------- .../src/__tests__/useQuery.test-d.ts | 3 +- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/packages/vue-query/src/__tests__/queryOptions.test-d.ts b/packages/vue-query/src/__tests__/queryOptions.test-d.ts index d7a4c6ca8f6..c40d606e862 100644 --- a/packages/vue-query/src/__tests__/queryOptions.test-d.ts +++ b/packages/vue-query/src/__tests__/queryOptions.test-d.ts @@ -125,17 +125,19 @@ describe('queryOptions', () => { it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { const { data } = reactive( - useQuery(queryOptions({ - queryKey: ['key'], - queryFn: () => { - return { + useQuery( + queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => ({ wow: true, - } - }, - initialData: () => ({ - wow: true, + }), }), - })), + ), ) expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() @@ -143,14 +145,16 @@ describe('queryOptions', () => { it('TData should have undefined in the union when initialData is NOT provided', () => { const { data } = reactive( - useQuery(queryOptions({ - queryKey: ['key'], - queryFn: () => { - return { - wow: true, - } - }, - })), + useQuery( + queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + }), + ), ) expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() @@ -158,15 +162,17 @@ describe('queryOptions', () => { it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { const { data } = reactive( - useQuery(queryOptions({ - queryKey: ['key'], - queryFn: () => { - return { - wow: true, - } - }, - initialData: () => undefined as { wow: boolean } | undefined, - })), + useQuery( + queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => undefined as { wow: boolean } | undefined, + }), + ), ) expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() @@ -174,7 +180,8 @@ describe('queryOptions', () => { it('TData should be narrowed after an isSuccess check when initialData is provided as a function which can return undefined', () => { const { data, isSuccess } = reactive( - useQuery(queryOptions({ + useQuery( + queryOptions({ queryKey: ['key'], queryFn: () => { return { @@ -182,8 +189,8 @@ describe('queryOptions', () => { } }, initialData: () => undefined as { wow: boolean } | undefined, - } - )), + }), + ), ) if (isSuccess) { @@ -193,10 +200,13 @@ describe('queryOptions', () => { it('data should not have undefined when initialData is provided', () => { const { data } = reactive( - useQuery(queryOptions({ - queryKey: ['query-key'], - initialData: 42, - }))) + useQuery( + queryOptions({ + queryKey: ['query-key'], + initialData: 42, + }), + ), + ) expectTypeOf(data).toEqualTypeOf() }) diff --git a/packages/vue-query/src/__tests__/useQuery.test-d.ts b/packages/vue-query/src/__tests__/useQuery.test-d.ts index dead867a172..3185a88957b 100644 --- a/packages/vue-query/src/__tests__/useQuery.test-d.ts +++ b/packages/vue-query/src/__tests__/useQuery.test-d.ts @@ -131,7 +131,8 @@ describe('useQuery', () => { useQuery({ queryKey: ['query-key'], initialData: 42, - })) + }), + ) expectTypeOf(data).toEqualTypeOf() })