Skip to content

Commit

Permalink
feat(types): expose some useful route location types
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Jul 7, 2022
1 parent 64ff5b6 commit 86b1d01
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 18 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,27 @@ The `RouterTyped` type gives you access to the typed version of the router insta
import type { RouterTyped } from '@vue-router'
```

#### `RouterLocationResolved`

The `RouterLocationResolved` type exposed by `@vue-router` allows passing a generic (which autocomplete) to type a route **whenever checking the name doesn't makes sense because you know the type**. This is useful for cases like `<RouterLink v-slot="{ route }">`:

```vue
<RouterLink v-slot="{ route }">
User {{ (route as RouterLocationResolved<'/users/[id]'>).params.id }}
</RouterLink>
```

This type corresponds to the return type of `router.resolve()`.

You have the same equivalents for `RouterLocation`, `RouterLocationNormalized`, and `RouterLocationNormalizedLoaded`. All of them exist in `vue-router` but the one exposed by `@vue-router` accept a generic:

```ts
// these are all valid
let userWithId: RouterLocationNormalizedLoaded<'/users/[id]'> = useRoute()
userWithId = useRoute<'/users/[id]'>()
userWithId = useRoute('/users/[id]') // 👈 this one is the easiest to write
```

## Named views

It is possible to define [named views](https://router.vuejs.org/guide/essentials/named-views.html#named-views) by appending an `@` + a name to their filename, e.g. a file named `src/routes/[email protected]` will generate a route of:
Expand Down
41 changes: 35 additions & 6 deletions playground/typed-router.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@
/// <reference types="unplugin-vue-router/client" />

import type {
_RouterTyped,
RouteRecordInfo,
RouterLinkTyped,
// type safe route locations
RouteLocationTypedList,
RouteLocationResolvedTypedList,
RouteLocationNormalizedTypedList,
RouteLocationNormalizedLoadedTypedList,
NavigationGuard,

// helper types
// route definitions
RouteRecordInfo,
ParamValue,
ParamValueOneOrMore,
ParamValueZeroOrMore,
ParamValueZeroOrOne,

// vue-router extensions
_RouterTyped,
RouterLinkTyped,
NavigationGuard,
UseLinkFnTyped,
} from 'unplugin-vue-router'

Expand Down Expand Up @@ -53,10 +62,30 @@ declare module '@vue-router' {
import type { RouteNamedMap } from '@vue-router/routes'

export type RouterTyped = _RouterTyped<RouteNamedMap>

/**
* Generate a type safe route location. Requires the name of the route to be passed as a generic.
* Type safe version of `RouteLocationNormalized` (the type of `to` and `from` in navigation guards).
* Allows passing the name of the route to be passed as a generic.
*/
export type Route<Name extends keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]
export type RouteLocationNormalized<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedTypedList<RouteNamedMap>[Name]

/**
* Type safe version of `RouteLocationNormalizedLoaded` (the return type of `useRoute()`).
* Allows passing the name of the route to be passed as a generic.
*/
export type RouteLocationNormalizedLoaded<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]

/**
* Type safe version of `RouteLocationResolved` (the returned route of `router.resolve()`).
* Allows passing the name of the route to be passed as a generic.
*/
export type RouteLocationResolved<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationResolvedTypedList<RouteNamedMap>[Name]

/**
* Type safe version of `RouteLocation` . Allows passing the name of the route to be passed as a generic.
*/
export type RouteLocation<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationTypedList<RouteNamedMap>[Name]

/**
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic.
*/
Expand Down
45 changes: 37 additions & 8 deletions src/core/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,34 +137,63 @@ export function createRoutesContext(options: ResolvedOptions) {
/// <reference types="unplugin-vue-router/client" />
import type {
_RouterTyped,
RouteRecordInfo,
RouterLinkTyped,
// type safe route locations
RouteLocationTypedList,
RouteLocationResolvedTypedList,
RouteLocationNormalizedTypedList,
RouteLocationNormalizedLoadedTypedList,
NavigationGuard,
// helper types
// route definitions
RouteRecordInfo,
ParamValue,
ParamValueOneOrMore,
ParamValueZeroOrMore,
ParamValueZeroOrOne,
// vue-router extensions
_RouterTyped,
RouterLinkTyped,
NavigationGuard,
UseLinkFnTyped,
} from 'unplugin-vue-router'
declare module '${MODULE_ROUTES_PATH}' {
${generateRouteNamedMap(routeTree)
.split('\n')
.filter((line) => line)
.map((line) => ' ' + line) // not the same as padStart(2)
.filter((line) => line) // remove empty lines
.map((line) => ' ' + line) // Indent by two spaces
.join('\n')}
}
declare module '${MODULE_VUE_ROUTER}' {
import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}'
export type RouterTyped = _RouterTyped<RouteNamedMap>
/**
* Generate a type safe route location. Requires the name of the route to be passed as a generic.
* Type safe version of \`RouteLocationNormalized\` (the type of \`to\` and \`from\` in navigation guards).
* Allows passing the name of the route to be passed as a generic.
*/
export type Route<Name extends keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]
export type RouteLocationNormalized<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedTypedList<RouteNamedMap>[Name]
/**
* Type safe version of \`RouteLocationNormalizedLoaded\` (the return type of \`useRoute()\`).
* Allows passing the name of the route to be passed as a generic.
*/
export type RouteLocationNormalizedLoaded<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]
/**
* Type safe version of \`RouteLocationResolved\` (the returned route of \`router.resolve()\`).
* Allows passing the name of the route to be passed as a generic.
*/
export type RouteLocationResolved<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationResolvedTypedList<RouteNamedMap>[Name]
/**
* Type safe version of \`RouteLocation\` . Allows passing the name of the route to be passed as a generic.
*/
export type RouteLocation<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationTypedList<RouteNamedMap>[Name]
/**
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic.
*/
Expand Down
11 changes: 8 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,19 @@ export type {
RouteRecordInfo,
} from './codegen/generateRouteMap'
export type {
RouteLocationNormalizedTyped,
RouteLocationNormalizedLoadedTyped,
RouteLocationNormalizedLoadedTypedList,
RouteLocationAsRelativeTyped,
RouteLocationAsRelativeTypedList,
RouteLocationAsPathTyped,
RouteLocationAsPathTypedList,
RouteLocationAsString,
RouteLocationTyped,
RouteLocationTypedList,
RouteLocationResolvedTyped,
RouteLocationResolvedTypedList,
RouteLocationNormalizedTyped,
RouteLocationNormalizedTypedList,
RouteLocationNormalizedLoadedTyped,
RouteLocationNormalizedLoadedTypedList,
} from './typeExtensions/routeLocation'
export type { NavigationGuard } from './typeExtensions/navigationGuards'
export type { _RouterTyped } from './typeExtensions/router'
Expand Down
1 change: 0 additions & 1 deletion src/typeExtensions/RouterLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export interface RouterLinkTyped<RouteMap extends _RouteMapGeneric> {
RouterLinkProps<RouteMap>

$slots: {
// TODO: is it correct to use the resolve tip?
default: (arg: UnwrapRef<_UseLinkReturnTyped<RouteMap>>) => VNode[]
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/typeExtensions/routeLocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export interface RouteLocationTyped<
params: RouteMap[Name]['params']
}

export type RouteLocationTypedList<
RouteMap extends _RouteMapGeneric = Record<string, RouteRecordInfo>
> = { [N in keyof RouteMap]: RouteLocationTyped<RouteMap, N> }

export interface RouteLocationResolvedTyped<
RouteMap extends _RouteMapGeneric,
Name extends keyof RouteMap
Expand Down

0 comments on commit 86b1d01

Please sign in to comment.