3.6 KiB
3.6 KiB
ts-use-from-param: Use from Parameter for Type Narrowing
Priority: CRITICAL
Explanation
When using hooks like useParams, useSearch, or useLoaderData, provide the from parameter to get exact types for that route. Without it, TypeScript returns a union of all possible types across all routes.
Bad Example
// Without 'from' - TypeScript doesn't know which route's types to use
function PostDetail() {
// params could be from ANY route - types are unioned
const params = useParams()
// params: { postId?: string; userId?: string; categoryId?: string; ... }
// TypeScript can't guarantee postId exists
console.log(params.postId) // postId: string | undefined
}
// Similarly for search params
function SearchResults() {
const search = useSearch()
// search: union of ALL routes' search params
}
Good Example
// With 'from' - exact types for this specific route
function PostDetail() {
const params = useParams({ from: '/posts/$postId' })
// params: { postId: string } - exactly what this route provides
console.log(params.postId) // postId: string (guaranteed)
}
// Full path matching
function UserPost() {
const params = useParams({ from: '/users/$userId/posts/$postId' })
// params: { userId: string; postId: string }
}
// Search params with type narrowing
function SearchResults() {
const search = useSearch({ from: '/search' })
// search: exactly the validated search params for /search route
}
// Loader data with type inference
function PostPage() {
const { post, comments } = useLoaderData({ from: '/posts/$postId' })
// Exact types from your loader function
}
Using Route.fullPath for Type Safety
// routes/posts/$postId.tsx
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => {
const post = await fetchPost(params.postId)
return { post }
},
component: PostComponent,
})
function PostComponent() {
// Use Route.fullPath for guaranteed type matching
const params = useParams({ from: Route.fullPath })
const { post } = useLoaderData({ from: Route.fullPath })
// Or use route-specific helper (preferred in same file)
const { postId } = Route.useParams()
const data = Route.useLoaderData()
}
Using getRouteApi for Code-Split Components
// components/PostDetail.tsx (separate file from route)
import { getRouteApi } from '@tanstack/react-router'
// Get type-safe access without importing the route
const postRoute = getRouteApi('/posts/$postId')
export function PostDetail() {
const params = postRoute.useParams()
// params: { postId: string }
const data = postRoute.useLoaderData()
// data: exact loader return type
const search = postRoute.useSearch()
// search: exact search param types
}
When to Use strict: false
// In shared components that work across multiple routes
function Breadcrumbs() {
// strict: false returns union types but allows component reuse
const params = useParams({ strict: false })
const location = useLocation()
// params may or may not have certain values
return (
<nav>
{params.userId && <span>User: {params.userId}</span>}
{params.postId && <span>Post: {params.postId}</span>}
</nav>
)
}
Context
- Always use
fromin route-specific components for exact types - Use
Route.useParams()/Route.useLoaderData()within route files - Use
getRouteApi()in components split from route files - Use
strict: falseonly in truly generic, cross-route components - The
frompath must match exactly (including params like$postId)