From 86b1d011ca0f44c73052a8ff8b9bd8a98f4cc6e9 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 7 Jul 2022 12:17:08 +0200 Subject: [PATCH] feat(types): expose some useful route location types --- README.md | 21 ++++++++++++++ playground/typed-router.d.ts | 41 ++++++++++++++++++++++---- src/core/context.ts | 45 ++++++++++++++++++++++++----- src/index.ts | 11 +++++-- src/typeExtensions/RouterLink.ts | 1 - src/typeExtensions/routeLocation.ts | 4 +++ 6 files changed, 105 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 4261a4725..9b430b946 100644 --- a/README.md +++ b/README.md @@ -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 ``: + +```vue + + User {{ (route as RouterLocationResolved<'/users/[id]'>).params.id }} + +``` + +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/index@aux.vue` will generate a route of: diff --git a/playground/typed-router.d.ts b/playground/typed-router.d.ts index 5428d2e3e..42b5ba542 100644 --- a/playground/typed-router.d.ts +++ b/playground/typed-router.d.ts @@ -6,15 +6,24 @@ /// 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' @@ -53,10 +62,30 @@ declare module '@vue-router' { import type { RouteNamedMap } from '@vue-router/routes' export type RouterTyped = _RouterTyped + /** - * 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 = RouteLocationNormalizedLoadedTypedList[Name] + export type RouteLocationNormalized = RouteLocationNormalizedTypedList[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 = RouteLocationNormalizedLoadedTypedList[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 = RouteLocationResolvedTypedList[Name] + + /** + * Type safe version of `RouteLocation` . Allows passing the name of the route to be passed as a generic. + */ + export type RouteLocation = RouteLocationTypedList[Name] + /** * Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. */ diff --git a/src/core/context.ts b/src/core/context.ts index 4b79d2742..d4d3e4afe 100644 --- a/src/core/context.ts +++ b/src/core/context.ts @@ -137,23 +137,32 @@ export function createRoutesContext(options: ResolvedOptions) { /// 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')} } @@ -161,10 +170,30 @@ declare module '${MODULE_VUE_ROUTER}' { import type { RouteNamedMap } from '${MODULE_ROUTES_PATH}' export type RouterTyped = _RouterTyped + /** - * 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 = RouteLocationNormalizedLoadedTypedList[Name] + export type RouteLocationNormalized = RouteLocationNormalizedTypedList[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 = RouteLocationNormalizedLoadedTypedList[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 = RouteLocationResolvedTypedList[Name] + + /** + * Type safe version of \`RouteLocation\` . Allows passing the name of the route to be passed as a generic. + */ + export type RouteLocation = RouteLocationTypedList[Name] + /** * Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. */ diff --git a/src/index.ts b/src/index.ts index 7031d1705..43b4989b1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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' diff --git a/src/typeExtensions/RouterLink.ts b/src/typeExtensions/RouterLink.ts index bf395ef28..45d5a07bd 100644 --- a/src/typeExtensions/RouterLink.ts +++ b/src/typeExtensions/RouterLink.ts @@ -48,7 +48,6 @@ export interface RouterLinkTyped { RouterLinkProps $slots: { - // TODO: is it correct to use the resolve tip? default: (arg: UnwrapRef<_UseLinkReturnTyped>) => VNode[] } } diff --git a/src/typeExtensions/routeLocation.ts b/src/typeExtensions/routeLocation.ts index a03bf4909..58d67e311 100644 --- a/src/typeExtensions/routeLocation.ts +++ b/src/typeExtensions/routeLocation.ts @@ -75,6 +75,10 @@ export interface RouteLocationTyped< params: RouteMap[Name]['params'] } +export type RouteLocationTypedList< + RouteMap extends _RouteMapGeneric = Record +> = { [N in keyof RouteMap]: RouteLocationTyped } + export interface RouteLocationResolvedTyped< RouteMap extends _RouteMapGeneric, Name extends keyof RouteMap