diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts index 9bd0f6ac77..225d9e45b2 100644 --- a/packages/react-router/src/route.ts +++ b/packages/react-router/src/route.ts @@ -750,37 +750,21 @@ export type RouteConstraints = { TRouteTree: AnyRoute } +export type RouteTypesById< + TRouter extends RegisteredRouter, + TId extends RouteIds, +> = RouteById['types'] + export function getRouteApi< - TId extends RouteIds, - TRouter extends AnyRouter = RegisteredRouter, - TRoute extends AnyRoute = RouteById, - TFullSearchSchema = TRoute['types']['fullSearchSchema'], - TAllParams = TRoute['types']['allParams'], - TAllContext = TRoute['types']['allContext'], - TLoaderDeps = TRoute['types']['loaderDeps'], - TLoaderData = TRoute['types']['loaderData'], + TRouter extends RegisteredRouter, + TId extends RouteIds, >(id: TId) { - return new RouteApi< - TId, - TRouter, - TRoute, - TFullSearchSchema, - TAllParams, - TAllContext, - TLoaderDeps, - TLoaderData - >({ id }) + return new RouteApi({ id }) } export class RouteApi< - TId extends RouteIds, - TRouter extends AnyRouter = RegisteredRouter, - TRoute extends AnyRoute = RouteById, - TFullSearchSchema = TRoute['types']['fullSearchSchema'], - TAllParams = TRoute['types']['allParams'], - TAllContext = TRoute['types']['allContext'], - TLoaderDeps = TRoute['types']['loaderDeps'], - TLoaderData = TRoute['types']['loaderData'], + TRouter extends RegisteredRouter, + TId extends RouteIds, > { id: TId @@ -801,8 +785,12 @@ export class RouteApi< return useMatch({ select: opts?.select, from: this.id }) } - useRouteContext = >(opts?: { - select?: (s: Expand) => TSelected + useRouteContext = < + TSelected = Expand['allContext']>, + >(opts?: { + select?: ( + s: Expand['allContext']>, + ) => TSelected }): TSelected => { return useMatch({ from: this.id, @@ -810,32 +798,44 @@ export class RouteApi< }) } - useSearch = >(opts?: { - select?: (s: Expand) => TSelected + useSearch = < + TSelected = Expand['fullSearchSchema']>, + >(opts?: { + select?: ( + s: Expand['fullSearchSchema']>, + ) => TSelected }): TSelected => { return useSearch({ ...opts, from: this.id }) } - useParams = >(opts?: { - select?: (s: Expand) => TSelected + useParams = < + TSelected = Expand['allParams']>, + >(opts?: { + select?: (s: Expand['allParams']>) => TSelected }): TSelected => { return useParams({ ...opts, from: this.id }) } - useLoaderDeps = (opts?: { - select?: (s: TLoaderDeps) => TSelected + useLoaderDeps = < + TSelected = RouteTypesById['loaderDeps'], + >(opts?: { + select?: (s: RouteTypesById['loaderDeps']) => TSelected }): TSelected => { return useLoaderDeps({ ...opts, from: this.id, strict: false } as any) } - useLoaderData = (opts?: { - select?: (s: TLoaderData) => TSelected + useLoaderData = < + TSelected = RouteTypesById['loaderData'], + >(opts?: { + select?: (s: RouteTypesById['loaderData']) => TSelected }): TSelected => { return useLoaderData({ ...opts, from: this.id, strict: false } as any) } - useNavigate = (): UseNavigateResult => { - return useNavigate({ from: this.id }) + useNavigate = (): UseNavigateResult< + RouteTypesById['fullPath'] + > => { + return useNavigate({ from: this.id as string }) } notFound = (opts?: NotFoundError) => { diff --git a/packages/react-router/tests/routeApi.test-d.tsx b/packages/react-router/tests/routeApi.test-d.tsx index 927cd6a536..467bfb7263 100644 --- a/packages/react-router/tests/routeApi.test-d.tsx +++ b/packages/react-router/tests/routeApi.test-d.tsx @@ -1,6 +1,6 @@ import { describe, expectTypeOf, test } from 'vitest' import { createRootRoute, createRoute, createRouter, getRouteApi } from '../src' -import type { UseNavigateResult } from '../src' +import type { MakeRouteMatch, UseNavigateResult } from '../src' const rootRoute = createRootRoute() @@ -23,6 +23,9 @@ const invoiceRoute = createRoute({ getParentRoute: () => invoicesRoute, path: '$invoiceId', validateSearch: () => ({ page: 0 }), + beforeLoad: () => ({ beforeLoadContext: 0 }), + loaderDeps: () => ({ dep: 0 }), + loader: () => ({ data: 0 }), }) const routeTree = rootRoute.addChildren([ @@ -40,7 +43,7 @@ type ExtractDefaultFrom = T extends UseNavigateResult ? DefaultFrom : never describe('getRouteApi', () => { - const invoiceRouteApi = getRouteApi<'/invoices/$invoiceId', DefaultRouter>( + const invoiceRouteApi = getRouteApi( '/invoices/$invoiceId', ) describe('useNavigate', () => { @@ -52,6 +55,36 @@ describe('getRouteApi', () => { >().toEqualTypeOf<'/invoices/$invoiceId'>() }) }) + test('useParams', () => { + expectTypeOf(invoiceRouteApi.useParams()).toEqualTypeOf<{ + invoiceId: string + }>() + }) + test('useContext', () => { + expectTypeOf(invoiceRouteApi.useRouteContext()).toEqualTypeOf<{ + beforeLoadContext: number + }>() + }) + test('useSearch', () => { + expectTypeOf(invoiceRouteApi.useSearch()).toEqualTypeOf<{ + page: number + }>() + }) + test('useLoaderData', () => { + expectTypeOf(invoiceRouteApi.useLoaderData()).toEqualTypeOf<{ + data: number + }>() + }) + test('useLoaderDeps', () => { + expectTypeOf(invoiceRouteApi.useLoaderDeps()).toEqualTypeOf<{ + dep: number + }>() + }) + test('useMatch', () => { + expectTypeOf(invoiceRouteApi.useMatch()).toEqualTypeOf< + MakeRouteMatch + >() + }) }) describe('createRoute', () => {