Skip to content

fix(nuxt): correct type inference when using transform with getCachedData#34800

Closed
noriyuki-shimizu wants to merge 2 commits intonuxt:mainfrom
noriyuki-shimizu:fix/use-async-data-type-inference-29567
Closed

fix(nuxt): correct type inference when using transform with getCachedData#34800
noriyuki-shimizu wants to merge 2 commits intonuxt:mainfrom
noriyuki-shimizu:fix/use-async-data-type-inference-29567

Conversation

@noriyuki-shimizu
Copy link
Copy Markdown
Contributor

🔗 Linked issue

Fixes #29567

📚 Description

Problem

When using useAsyncData with both transform and getCachedData, TypeScript incorrectly infers DataT from getCachedData's return type instead of from the transform function.

const { data } = useAsyncData(
  'key',
  () => $fetch<{ foo: string }>('/api'),
  {
    transform: response => response.foo, // DataT should be string
    getCachedData: key => useNuxtApp().payload.data[key],
  },
)
// data was incorrectly typed as Ref<{ foo: string } | undefined>
// instead of Ref<string | undefined>

Solution

Two changes in asyncData.ts:

  1. Remove custom NoInfer type — The custom implementation [T][T extends any ? 0 : never] did not fully prevent inference leakage when undefined was part of the union. TypeScript 5.4+ provides a built-in NoInfer utility type that handles this correctly.

  2. Move undefined inside NoInfer — Changed getCachedData return type from NoInfer<DataT> | undefined to NoInfer<DataT | undefined>. This ensures TypeScript does not use getCachedData's return type as an inference site for DataT.

A regression test was added to verify DataT is correctly inferred from transform when getCachedData is also provided.

Notes

@bolt-new-by-stackblitz
Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@github-actions github-actions Bot added 5.x 🐛 bug Something isn't working as expected labels Apr 12, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 12, 2026

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@34800

@nuxt/nitro-server

npm i https://pkg.pr.new/@nuxt/nitro-server@34800

nuxt

npm i https://pkg.pr.new/nuxt@34800

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@34800

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@34800

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@34800

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@34800

commit: 77143a1

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 12, 2026

Merging this PR will not alter performance

✅ 20 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing noriyuki-shimizu:fix/use-async-data-type-inference-29567 (77143a1) with main (95e6b9f)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@noriyuki-shimizu noriyuki-shimizu marked this pull request as ready for review April 12, 2026 10:30
@noriyuki-shimizu
Copy link
Copy Markdown
Contributor Author

@danielroe
The CI failure on Windows (fixtures:vite-dev-*) is unrelated to this change — it's a flaky test caused by an EADDRINUSE port conflict in the CI environment. Could you re-run the failed jobs? 🙏

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 56fd0ded-a7df-478a-8d3b-849f7aa83a73

📥 Commits

Reviewing files that changed from the base of the PR and between ab0a4e2 and 77143a1.

📒 Files selected for processing (1)
  • test/fixtures/basic-types/app/app-types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/fixtures/basic-types/app/app-types.ts

Walkthrough

The pull request changes type declarations for useAsyncData: it removes the exported NoInfer type alias and updates AsyncDataOptions.getCachedData return type from NoInfer<DataT> | undefined to NoInfer<DataT | undefined>. A new type-inference test was added to verify that useAsyncData infers data: Ref<DataT | DefaultAsyncDataValue> from the transform return type when getCachedData is provided.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: fixing type inference for useAsyncData when both transform and getCachedData are used together.
Description check ✅ Passed The description clearly explains the problem, the solution with specific code changes, and references the linked issue #29567 that prompted this PR.
Linked Issues check ✅ Passed The PR fully addresses the objectives from issue #29567: it removes the custom NoInfer implementation, moves undefined inside NoInfer to prevent inference leakage, maintains TS version compatibility, and adds regression tests.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the type inference issue in asyncData.ts and adding a regression test; no unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@danielroe
Copy link
Copy Markdown
Member

thank you ❤️

@danielroe danielroe added this pull request to the merge queue Apr 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 12, 2026
@noriyuki-shimizu
Copy link
Copy Markdown
Contributor Author

@danielroe
I've also left a comment on the related PR 🙋 #34123: #34123 (comment)

@afurm
Copy link
Copy Markdown

afurm commented Apr 13, 2026

The regression test uses .toEqualTypeOf<string | DefaultAsyncDataValue>() — this passes whenever the actual type is any supertype of string. Should this be .toBeString() or an explicit expectTypeOf<Ref<string | undefined>>() to actually verify the fix?

@danielroe danielroe added this pull request to the merge queue Apr 15, 2026
@danielroe danielroe removed this pull request from the merge queue due to a manual request Apr 15, 2026
@danielroe
Copy link
Copy Markdown
Member

@noriyuki-shimizu checking, the regression test actually seems to pass on the main branch - would you investigate please? 🙏

@noriyuki-shimizu
Copy link
Copy Markdown
Contributor Author

@danielroe @afurm
Thanks for the review! You're right — the original test was not meaningful because payload.data[key] returns any, so the NoInfer implementation didn't matter.

I investigated thoroughly and updated the regression test with the following improvements:

  1. getCachedData now returns a typed value — changed to useNuxtApp().payload.data[key] as string | undefined instead of the untyped any from payload.data[key]. This ensures the test actually exercises the NoInfer behavior on getCachedData's return type.
  2. Assertion targets Ref directly — changed from expectTypeOf(data.value).toEqualTypeOf<string | DefaultAsyncDataValue>() to expectTypeOf(data).toEqualTypeOf<Ref<string | DefaultAsyncDataValue>>() for a more precise check.

Important note: during investigation, I confirmed that the exact reproduction from issue #29567 (explicit (key: string) parameter annotation + typed return in getCachedData) actually fails on both branches due to a TypeScript inference limitation unrelated to NoInfer. The current test avoids the explicit parameter annotation to focus specifically on verifying that the NoInfer change prevents inference leakage from getCachedData's return type into DataT.

That said, I should be transparent: this test also passes on main because TypeScript's built-in overload resolution already handles this particular scenario correctly even with the old custom NoInfer. The code change itself (removing the custom NoInfer in favor of the built-in one + moving undefined inside NoInfer) is still a valid cleanup for TypeScript 5.4+, but I was unable to construct a type test that differentiates the two implementations. I'm open to removing the test if you'd prefer, or keeping it as documentation of the expected behavior. Happy to hear your thoughts.

@noriyuki-shimizu
Copy link
Copy Markdown
Contributor Author

@danielroe
The CI failure in test/basic.test.ts:2783 (Native Async Context — "hasApp": true vs false) is unrelated to this PR's changes, which only affect type definitions. This appears to be a flaky test. Could you re-run the CI? 🙏

@github-actions
Copy link
Copy Markdown
Contributor

We've flagged this as a potential contribution without a human behind it. We welcome the thoughtful use of AI tools when contributing to Nuxt, but ask all contributors to follow two core principles:

  1. Never let an LLM speak for you - all comments, issues, and PR descriptions should be written in your own words, reflecting your own understanding.
  2. Never let an LLM think for you - only submit contributions you fully understand and can explain.

Please review our AI-assisted contribution guidelines and update this contribution if needed.

If this was flagged in error, we apologise! 😳 Just let us know. 🙏

@danielroe
Copy link
Copy Markdown
Member

as far as I understand it, the test you've added passes on both main and this branch - and this PR doesn't solve the linked issue. is that right?

@noriyuki-shimizu
Copy link
Copy Markdown
Contributor Author

@danielroe
Yes, that's correct. After thorough investigation, I confirmed that:

  1. The test passes on both main and this branch. I was unable to construct a type test that differentiates the old custom NoInfer from the built-in one.

  2. This PR does not fully solve issue Incorrect type inference in useAsyncData with transform and getCachedData #29567. The exact reproduction from the issue (combining explicit (key: string) parameter annotation + typed return value in getCachedData + transform) fails on both branches. The root cause appears to be a TypeScript inference limitation when multiple callback parameter types compete for inference of the same type parameter, which is not something NoInfer alone can fix.

The changes in this PR (removing the custom NoInfer in favor of the built-in one + moving undefined inside NoInfer) are still valid as a cleanup for TypeScript 5.4+, but I should not have linked this as a fix for #29567.

I'm happy to either close this PR or rework it as a simple cleanup (removing the issue link), whichever you prefer.

@danielroe
Copy link
Copy Markdown
Member

are you a bot or a human?

@noriyuki-shimizu
Copy link
Copy Markdown
Contributor Author

I'm a human 🙂
Though I should be transparent — I used AI to help draft the summary and investigate the type behavior. The analysis and conclusions are based on actual testing I did locally, but the writeup was AI-assisted.🙏

@danielroe
Copy link
Copy Markdown
Member

okay, that’s all good! just wanted to check there was a ‘responsible adult’ in the room 😆

i am not sure there is a reason to merge this PR if we can’t reproduce a bug that it fixes

maybe worth some more time thinking about it 🙏

@afurm
Copy link
Copy Markdown

afurm commented Apr 15, 2026

danielroe raises a valid point — if the test passes on both branches, the PR is currently a cleanup without a regression test for the reported bug. The type change itself (moving undefined inside NoInfer) is sound for TypeScript 5.4+, but without a failing test case that distinguishes the old behavior from the new, there's no way to prevent future regressions. If #29567 is still worth solving, it would need a more targeted reproduction that actually fails on main but passes with the fix.

@danielroe
Copy link
Copy Markdown
Member

... @afurm are you a human?

@afurm
Copy link
Copy Markdown

afurm commented Apr 15, 2026

Yes, human 🙂 I occasionally use AI assistance to draft comments faster, but all technical opinions are mine and I verify things locally before posting.

@danielroe danielroe closed this Apr 16, 2026
@noriyuki-shimizu noriyuki-shimizu deleted the fix/use-async-data-type-inference-29567 branch April 16, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5.x 🐛 bug Something isn't working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect type inference in useAsyncData with transform and getCachedData

3 participants