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:
constresult: UseQueryResult<{wow: string}>=useQuery({queryKey: ['key'],queryFn: ()=>({wow: true}),initialData: ()=>undefinedas{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:
typeResult=|{type: 'first';first: string}|{type: 'second';second: string}constquery=useQuery({queryKey: ['example'],queryFn: (): Result=>({type: 'first',first: 'a'}),})constsecond=query.data?.type==='first' ? undefined : query.datasecond?.secondThis 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:
typeNarrowableNoInfer<T>=Textendsunknown ? 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