Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix isRoute return type for non exact matches #357

Merged
merged 10 commits into from
Dec 9, 2024
14 changes: 11 additions & 3 deletions src/guards/routes.spec-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ test('router route can be narrowed', () => {
path: '/parentB',
component,
}),
]
] as const

const { route } = createRouter(routes)

Expand All @@ -41,12 +41,20 @@ test('router route can be narrowed', () => {
expectTypeOf<typeof route.name>().toMatchTypeOf<'parentA'>()
}

if (isRoute(route, 'parentB', { exact: true })) {
expectTypeOf<typeof route.name>().toMatchTypeOf<'parentB'>()
}

if (isRoute(route, 'parentA', { exact: false })) {
expectTypeOf<typeof route.name>().toMatchTypeOf<'parentA' | 'parentA.childA'>()
expectTypeOf<typeof route.name>().toMatchTypeOf<'parentA' | 'childA'>()
}

if (isRoute(route, 'parentB', { exact: false })) {
expectTypeOf<typeof route.name>().toMatchTypeOf<'parentB'>()
}

if (isRoute(route, 'parentA')) {
expectTypeOf<typeof route.name>().toMatchTypeOf<'parentA' | 'parentA.childA'>()
expectTypeOf<typeof route.name>().toMatchTypeOf<'parentA' | 'childA'>()
}

if (route.name === 'parentA') {
Expand Down
24 changes: 14 additions & 10 deletions src/guards/routes.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
import { RouterRoute, isRouterRoute } from '@/services/createRouterRoute'
import { toName } from '@/types/name'
import { RegisteredRouterRoute, RegisteredRoutesName } from '@/types/register'

export type IsRouteOptions = {
exact?: boolean,
}

type RouteWithMatch<
TRoute extends RouterRoute,
TRouteName extends TRoute['name']
> = TRoute extends RouterRoute
? TRouteName extends TRoute['matches'][number]['name']
? TRoute
: never
: never

export function isRoute(route: unknown): route is RouterRoute

export function isRoute<
TRoute extends RouterRoute,
TRouteName extends TRoute['name']
>(route: TRoute, routeName: TRouteName, options: IsRouteOptions & { exact: true }): route is TRoute & { name: TRouteName }
>(route: TRoute, routeName: TRouteName, options: IsRouteOptions & { exact: true }): route is TRoute & {name: TRouteName}

export function isRoute<
TRoute extends RouterRoute,
TRouteName extends TRoute['name']
>(route: TRoute, routeName: TRouteName, options?: IsRouteOptions): route is TRoute & { name: `${TRouteName}${string}` }
>(route: TRoute, routeName: TRouteName, options?: IsRouteOptions): route is RouteWithMatch<TRoute, TRouteName>

export function isRoute<
TRouteName extends RegisteredRoutesName
>(route: unknown, routeName: TRouteName, options: IsRouteOptions & { exact: true }): route is RegisteredRouterRoute & { name: TRouteName }

export function isRoute<
TRouteName extends RegisteredRoutesName
>(route: unknown, routeName: TRouteName, options?: IsRouteOptions): route is RegisteredRouterRoute & { name: `${TRouteName}${string}` }
>(route: unknown, routeName: TRouteName, options?: IsRouteOptions): route is RouteWithMatch<RegisteredRouterRoute, TRouteName>

export function isRoute(route: unknown, routeName?: string, options?: IsRouteOptions): boolean

Expand All @@ -37,13 +45,9 @@ export function isRoute(route: unknown, routeName?: string, { exact }: IsRouteOp
return true
}

const names = route.matches.map((route) => toName(route.name))

if (exact) {
const actualRouteName = names.at(-1)

return routeName === actualRouteName
return route.matched.name === routeName
}

return names.includes(routeName)
return route.matches.map((route) => route.name).includes(routeName)
}
Loading