TanStack / router

πŸ€– Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering.
https://tanstack.com/router
MIT License
7.44k stars 519 forks source link

Unexpected behaviour when returning `defer()` value without wrapping in an object #1818

Open mirague opened 2 weeks ago

mirague commented 2 weeks ago

Describe the bug

When returning a deferred promise from the route loader() the promise isn't actually deferred, but it seems awaited. This can be seen on the resolved type depending on whether the defer() value was returned directly vs. when it's wrapped inside an object.

This works:

loader: () => {
  return { 
    deferredPromise: defer(new Promise<string>(() => {})),
  }
}

const data = Route.useLoaderData() // βœ… type for `data.deferredPromise` is correctly `DeferredPromise<string>`

This does not work:

loader: () => {
  return defer(new Promise<string>(() => {}))
}

const deferredPromise = Route.useLoaderData() // ❌ type for `deferredPromise` is unexpectedly `string` instead of  `DeferredPromise<string>`

Your Example Website or App

https://stackblitz.com/edit/tanstack-router-hbzw42?file=src%2Froutes%2Fposts.%24postId.tsx

Steps to Reproduce the Bug or Issue

  1. Open the routes/posts/posts.$postId.tsx file
  2. Check out the comments in the loader
  3. Observe the difference between the types from Route.useLoaderData() depending on whether a defer(...) value is returned directly vs. when it's wrapped in an object, e.g. { deferredPromise: defer(...) }

Expected behavior

I expect the loaderData value for the defer(...) value to always be a DeferredPromise<>, not the value of said DeferredPromise.

Screenshots or Videos

CleanShot 2024-06-23 at 00 17 38@2x CleanShot 2024-06-23 at 00 17 23@2x

Platform

Additional context

No response

schiller-manuel commented 2 weeks ago

does returning a "blank" DeferredPromise even work correctly at runtime?

mirague commented 2 weeks ago

Not when directly returned, but it does when wrapped in an object. The blank promise is only for illustration/repro.