Context
useQuery currently returns UseQueryResult<NoInfer<TData>, TError>. That NoInfer guard is important: it prevents reverse inference from an annotated result type.
Without the guard, TypeScript can accept an incorrect annotation like this by letting the annotation influence TData:
const result: UseQueryResult<{ wow: string }> = useQuery({
queryKey: ['key'],
queryFn: () => ({ wow: true }),
initialData: () => undefined as { wow: boolean } | undefined,
})
That should be a type error because the query options produce { wow: boolean }, not { wow: string }. That is the behavior fixed in #8683.
Describe the bug
The regression is that after #10593 switched this return type to TypeScript's built-in NoInfer, discriminated-union narrowing on data no longer works in normal code:
type Result =
| { type: 'first'; first: string }
| { type: 'second'; second: string }
const query = useQuery({
queryKey: ['example'],
queryFn: (): Result => ({ type: 'first', first: 'a' }),
})
const second = query.data?.type === 'first' ? undefined : query.data
second?.second
This is not asking to remove NoInfer. Removing it would regress the reverse-inference guard. The issue is that built-in NoInfer<TData> on the return type appears to preserve that guard while also blocking useful union narrowing.
Your minimal, reproducible example
https://www.typescriptlang.org/play/?ts=5.9.3#code/C4TwDgpgBAgg7gQwJbAgEwGoIDYFcIA8AKgHxQC8URUEAHqgHZoDOUACgE4D2Atks4SQMAZhA5QAqmQD8kqAC4qAKFCRJAgIr4OIAEoRmubMGIARBMARlKAbyhoLCRUXOWoAHyi4mEYUPRQAL4q4NAA8mDASFwMzMRaYiAAYgyuCAA0VGkUsIgo6Fh4hEQJOilpJNZQNkpQUACO2skMigAUAJQUZCVN5Y61UEIoSDhp0m2d5GTwyKiYOPjxvamOZJ7eaL7+aErBqtAAcggc3IgARtgQB1wAkiJixFXUdIwsXgwA1gxccAxQstc7qIOI8FFAGBAAG5iJRKTYAY2wx2gwm88KiMS8miaABkuFwBEtEn03JRvF8fgxMi5HDkZvl5kUiWUVpZKq0BlxItFYooIhjYszmmlqRV0kp2ooJNjEvpDMYzKs4RBEcioKiGOieViIKVkhBNeghSSEDlyd9fqLaZR6XNCosesTWVYSBy6lyBcw+dyYnFHSyRVlVuLJepdU05UYTID7iCaWySMrVRwUWiBTq9aZ+MAOEgzrg5sbnWbPhaqUHSblZgUFsU9Sb2ZyfbyoPyeX7686rWyQ1KZTpIwqjicfggLldbrHFQnYfDfcAoNh8QIAOrcBgAcz74dlBijBDscB+imYOaEG6CVVw-ZAeIJEFaNTqjSdEy6UEfUCPcEUOfwQXacU6iGKJRkcN8pneTY-AhNAoAQVhD2PKAznxS4ED+QIPCgrZYPFQJ2lhAB6IioAAAWAZgAFo6EgdEaJHcQAHF8TQX8AAtDknYF1QNeFoBTAArFVKKgFNoQ4ARBljPiIAAOiUOdYgXURDTQNcYi3MM9UHEwkJ-KBT1zTdLzNG8klktBHwGF8WQgshP2-X8OH-AigOk4YwMseycJggIEOqL9kNQrh0Mw7CNlw9B8MIkjyMomjaDo4AGO4ZjWI46A0GzXN8yiaEvw4BAwEgcQPggCAwFYYBOMMhAeBRWSFKU097ByvMC3QDTN23HS9wVfSTzPEzAivG8syMjq5ms59lh8xzkL-aA3IGECRmwNIfMivy4ICwaULQiAMKCCKfB2mLYX2KBdIoAZPDsfZFAAcj8STgCegBudUkDeobjIvYI6nuqBHqgJ6BCUtBPsMlUYjYwzhoB2d5xQ7hyr+MlzMsmaGjmj9QxuyDP1Bl6ftPJ7Mle09noQJ6AIulqFzONGDQAZVhpgcmZrh0bkhxLGkOSrvIEWwap97-l87YwW53n+YQJRZbZjm0EFiG4eR5TvtoAJMZ3HQJrPPL0Bx2zmgmRRCYch7Qme8WKe+36wdp+ndkIxntfQdnIZyPwdbQPnHEF4XRdJt66dkbbpcUP30EDywlFjtBvbhtWVaUIA
Steps to reproduce
- Open the TypeScript Playground link.
- Notice that the
useQueryLoose example incorrectly accepts the wrong UseQueryResult<{ wow: string }> annotation.
- Notice that the
useQueryFenced example correctly rejects that wrong annotation, but brokenSecond?.second fails to type-check.
- Notice that the
useQueryDistributed example still rejects the wrong annotation and allows the discriminated-union narrowing case.
Expected behavior
query.data?.type === 'first' ? undefined : query.data should narrow to { type: 'second'; second: string } | undefined, so second?.second should type-check.
At the same time, an incorrect contextual annotation like UseQueryResult<{ wow: string }> should still be rejected when the query options produce { wow: boolean }.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
- TypeScript type-checking
- Every runtime platform
Tanstack Query adapter
React Query
TanStack Query version
5.101.2
TypeScript version
5.9.3
Additional context
A possible fix direction is a distributive wrapper:
type NarrowableNoInfer<T> = T extends unknown ? NoInfer<T> : never
The playground shows this preserving the reverse-inference guard while restoring discriminated-union narrowing.
Related context:
Context
useQuerycurrently returnsUseQueryResult<NoInfer<TData>, TError>. ThatNoInferguard is important: it prevents reverse inference from an annotated result type.Without the guard, TypeScript can accept an incorrect annotation like this by letting the annotation influence
TData:That should be a type error because the query options produce
{ wow: boolean }, not{ wow: string }. That is the behavior fixed in #8683.Describe the bug
The regression is that after #10593 switched this return type to TypeScript's built-in
NoInfer, discriminated-union narrowing ondatano longer works in normal code:This is not asking to remove
NoInfer. Removing it would regress the reverse-inference guard. The issue is that built-inNoInfer<TData>on the return type appears to preserve that guard while also blocking useful union narrowing.Your minimal, reproducible example
https://www.typescriptlang.org/play/?ts=5.9.3#code/C4TwDgpgBAgg7gQwJbAgEwGoIDYFcIA8AKgHxQC8URUEAHqgHZoDOUACgE4D2Atks4SQMAZhA5QAqmQD8kqAC4qAKFCRJAgIr4OIAEoRmubMGIARBMARlKAbyhoLCRUXOWoAHyi4mEYUPRQAL4q4NAA8mDASFwMzMRaYiAAYgyuCAA0VGkUsIgo6Fh4hEQJOilpJNZQNkpQUACO2skMigAUAJQUZCVN5Y61UEIoSDhp0m2d5GTwyKiYOPjxvamOZJ7eaL7+aErBqtAAcggc3IgARtgQB1wAkiJixFXUdIwsXgwA1gxccAxQstc7qIOI8FFAGBAAG5iJRKTYAY2wx2gwm88KiMS8miaABkuFwBEtEn03JRvF8fgxMi5HDkZvl5kUiWUVpZKq0BlxItFYooIhjYszmmlqRV0kp2ooJNjEvpDMYzKs4RBEcioKiGOieViIKVkhBNeghSSEDlyd9fqLaZR6XNCosesTWVYSBy6lyBcw+dyYnFHSyRVlVuLJepdU05UYTID7iCaWySMrVRwUWiBTq9aZ+MAOEgzrg5sbnWbPhaqUHSblZgUFsU9Sb2ZyfbyoPyeX7686rWyQ1KZTpIwqjicfggLldbrHFQnYfDfcAoNh8QIAOrcBgAcz74dlBijBDscB+imYOaEG6CVVw-ZAeIJEFaNTqjSdEy6UEfUCPcEUOfwQXacU6iGKJRkcN8pneTY-AhNAoAQVhD2PKAznxS4ED+QIPCgrZYPFQJ2lhAB6IioAAAWAZgAFo6EgdEaJHcQAHF8TQX8AAtDknYF1QNeFoBTAArFVKKgFNoQ4ARBljPiIAAOiUOdYgXURDTQNcYi3MM9UHEwkJ-KBT1zTdLzNG8klktBHwGF8WQgshP2-X8OH-AigOk4YwMseycJggIEOqL9kNQrh0Mw7CNlw9B8MIkjyMomjaDo4AGO4ZjWI46A0GzXN8yiaEvw4BAwEgcQPggCAwFYYBOMMhAeBRWSFKU097ByvMC3QDTN23HS9wVfSTzPEzAivG8syMjq5ms59lh8xzkL-aA3IGECRmwNIfMivy4ICwaULQiAMKCCKfB2mLYX2KBdIoAZPDsfZFAAcj8STgCegBudUkDeobjIvYI6nuqBHqgJ6BCUtBPsMlUYjYwzhoB2d5xQ7hyr+MlzMsmaGjmj9QxuyDP1Bl6ftPJ7Mle09noQJ6AIulqFzONGDQAZVhpgcmZrh0bkhxLGkOSrvIEWwap97-l87YwW53n+YQJRZbZjm0EFiG4eR5TvtoAJMZ3HQJrPPL0Bx2zmgmRRCYch7Qme8WKe+36wdp+ndkIxntfQdnIZyPwdbQPnHEF4XRdJt66dkbbpcUP30EDywlFjtBvbhtWVaUIA
Steps to reproduce
useQueryLooseexample incorrectly accepts the wrongUseQueryResult<{ wow: string }>annotation.useQueryFencedexample correctly rejects that wrong annotation, butbrokenSecond?.secondfails to type-check.useQueryDistributedexample still rejects the wrong annotation and allows the discriminated-union narrowing case.Expected behavior
query.data?.type === 'first' ? undefined : query.datashould narrow to{ type: 'second'; second: string } | undefined, sosecond?.secondshould type-check.At the same time, an incorrect contextual annotation like
UseQueryResult<{ wow: string }>should still be rejected when the query options produce{ wow: boolean }.How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Tanstack Query adapter
React Query
TanStack Query version
5.101.2
TypeScript version
5.9.3
Additional context
A possible fix direction is a distributive wrapper:
The playground shows this preserving the reverse-inference guard while restoring discriminated-union narrowing.
Related context:
useQuery#8683NoInferchange that appears to introduce this regression: fix(query-core): use built-in NoInfer for generic indexed-access types #10593