ErrorTypes,
} from '../src/errors'
import { components, tick } from './utils'
-import { RouteRecordRaw, NavigationGuard } from '../src/types'
+import type { RouteRecordRaw, NavigationGuard } from '../src'
import type {
RouteLocationRaw,
RouteLocationNormalized,
import fakePromise from 'faked-promise'
import { createDom, noGuard, newRouter as createRouter } from '../utils'
-import { RouteRecordRaw, NavigationGuard } from '../../src/types'
+import type { RouteRecordRaw, NavigationGuard } from '../../src'
const Home = { template: `<div>Home</div>` }
const Foo = { template: `<div>Foo</div>` }
import { extractComponentsGuards } from '../../src/navigationGuards'
-import { RouteRecordRaw } from '../../src/types'
+import type { RouteRecordRaw, RouteRecordNormalized } from '../../src'
import { START_LOCATION_NORMALIZED } from '../../src/location'
import { components } from '../utils'
import { normalizeRouteRecord } from '../../src/matcher'
-import { RouteRecordNormalized } from 'src/matcher/types'
import { mockWarn } from 'jest-mock-warn'
const beforeRouteEnter = jest.fn()
import { JSDOM, ConstructorOptions } from 'jsdom'
import {
- NavigationGuard,
RouteRecordMultipleViews,
MatcherLocation,
- RouteLocationNormalized,
RouteComponent,
RouteRecordRaw,
RouteRecordName,
Router,
RouterView,
RouteRecordNormalized,
+ NavigationGuard,
+ RouteLocationNormalized,
} from '../src'
export const tick = (time?: number) =>
// @ts-ignore
ComponentOptionsMixin,
} from 'vue'
-import {
- isRouteLocation,
- RouteLocationRaw,
- VueUseOptions,
- RouteLocation,
- RouteLocationNormalized,
-} from './types'
+import { isRouteLocation, type VueUseOptions } from './types'
import { isSameRouteLocationParams, isSameRouteRecord } from './location'
import { routerKey, routeLocationKey } from './injectionSymbols'
import { RouteRecord } from './matcher/types'
import { NavigationFailure } from './errors'
import { isArray, isBrowser, noop } from './utils'
import { warn } from './warning'
+import {
+ RouteLocation,
+ RouteLocationNormalized,
+ RouteLocationRaw,
+} from './typed-routes'
export interface RouterLinkOptions {
/**
}
export interface UseLinkDevtoolsContext {
+ // TODO: loaded type ?
route: RouteLocationNormalized & { href: string }
isActive: boolean
isExactActive: boolean
hasPrevious = true
}
- return router.resolve(to)
+ return router.resolve(
+ // @ts-expect-error: FIXME: errors on the name because of typed routes
+ to
+ )
})
const activeRecordIndex = computed<number>(() => {
VNode,
Component,
} from 'vue'
-import {
+import type {
RouteLocationNormalized,
RouteLocationNormalizedLoaded,
- RouteLocationMatched,
-} from './types'
+} from './typed-routes'
+import type { RouteLocationMatched } from './types'
import {
matchedRouteKey,
viewDepthKey,
NavigationGuardWithThis,
NavigationGuard,
RouteLocationNormalizedLoaded,
-} from './types'
-import { RouterView } from './RouterView'
-import { RouterLink } from './RouterLink'
+} from './typed-routes'
+import type { RouterView } from './RouterView'
+import type { RouterLink } from './RouterLink'
import type { Router } from './router'
import type { TypesConfig } from './config'
export { createRouterMatcher } from './matcher'
export type { RouterMatcher } from './matcher'
-export type * from './typed-routes'
-
export { parseQuery, stringifyQuery } from './query'
export type {
LocationQuery,
MatcherLocationAsPath,
LocationAsRelativeRaw,
RouteQueryAndHash,
- RouteLocationRaw,
- RouteLocation,
- RouteLocationNormalized,
- RouteLocationNormalizedLoaded,
- RouteParams,
- RouteParamsRaw,
+
+ // route params
RouteParamValue,
RouteParamValueRaw,
+
+ // Partial route location
RouteLocationNamedRaw,
+ // exported for backwards compat for old RouteLocationRaw
RouteLocationPathRaw,
RouteLocationMatched,
+
+ // extra options when navigating
RouteLocationOptions,
+
// route records
_RouteRecordBase,
- RouteRecordName,
RouteRecordRaw,
RouteRecordSingleView,
RouteRecordSingleViewWithChildren,
RouteMeta,
RouteComponent,
// RawRouteComponent,
- NavigationGuard,
NavigationGuardNext,
+} from './types'
+
+// Experimental Type Safe API
+export type {
+ // route location
+ RouteLocationRaw,
+ RouteLocation,
+ RouteLocationNormalized,
+ RouteLocationNormalizedLoaded,
+ RouteLocationResolved,
+ RouteLocationAsRelativePath,
+
+ // route records
+ RouteRecordInfo,
+ RouteRecordName,
+ RouteRecordRedirectOption,
+
+ // params
+ RouteParams,
+ RouteParamsRaw,
+ ParamValue,
+ ParamValueOneOrMore,
+ ParamValueZeroOrMore,
+ ParamValueZeroOrOne,
+
+ // navigation guards
+ NavigationGuard,
NavigationGuardWithThis,
NavigationHookAfter,
-} from './types'
+ NavigationGuardReturn,
+} from './typed-routes'
export { createRouter } from './router'
export type { Router, RouterOptions, RouterScrollBehavior } from './router'
+import type { NavigationGuard } from '../typed-routes'
import {
RouteRecordMultipleViews,
- NavigationGuard,
_RouteRecordBase,
_RouteRecordProps,
NavigationGuardNextCallback,
import {
- NavigationGuard,
NavigationGuardNext,
NavigationGuardNextCallback,
isRouteLocation,
Lazy,
RouteComponent,
RawRouteComponent,
+
+ // NOTE: Still need to use some old types while migrating
+ RouteLocationRaw as RouteLocationRaw_OLD,
} from './types'
+
import type {
- RouteLocationRaw,
RouteLocationNormalized,
RouteLocationNormalizedLoaded,
+ NavigationGuard,
} from './typed-routes'
import {
return () =>
new Promise((resolve, reject) => {
const next: NavigationGuardNext = (
- valid?: boolean | RouteLocationRaw | NavigationGuardNextCallback | Error
+ valid?:
+ | boolean
+ // TODO: remove
+ | RouteLocationRaw_OLD
+ | NavigationGuardNextCallback
+ | Error
) => {
if (valid === false) {
reject(
import {
RouteRecordRaw,
- NavigationHookAfter,
Lazy,
isRouteLocation,
isRouteName,
- NavigationGuardWithThis,
RouteLocationOptions,
MatcherLocationRaw,
} from './types'
RouteParams,
RouteLocationNormalized,
RouteLocationNormalizedLoaded,
+ NavigationGuardWithThis,
+ NavigationHookAfter,
+ RouteLocationResolved,
} from './typed-routes'
import { RouterHistory, HistoryState, NavigationType } from './history/common'
import {
} from './injectionSymbols'
import { addDevtools } from './devtools'
import { _LiteralUnion } from './types/utils'
+import {
+ RouteLocationAsPathTyped,
+ RouteLocationAsRelativeTyped,
+ RouteLocationAsString,
+} from './typed-routes/route-location'
+import { RouteMap } from './typed-routes/route-map'
/**
* Internal type to define an ErrorHandler
readonly options: RouterOptions
/**
- * Allows turning off the listening of history events. This is a low level api for micro-frontends.
+ * Allows turning off the listening of history events. This is a low level api for micro-frontend.
*/
listening: boolean
* @param to - Raw route location to resolve
* @param currentLocation - Optional current location to resolve against
*/
- resolve(
- to: RouteLocationRaw,
+ resolve<Name extends keyof RouteMap = keyof RouteMap>(
+ to:
+ | RouteLocationAsString<RouteMap>
+ | RouteLocationAsRelativeTyped<RouteMap, Name>
+ | RouteLocationAsPathTyped<RouteMap, Name>,
currentLocation?: RouteLocationNormalizedLoaded
- ): RouteLocation & { href: string }
+ ): RouteLocationResolved<Name>
/**
* Programmatically navigate to a new URL by pushing an entry in the history
}
function resolve(
+ // NOTE: it's easier to by pass the type generics which are just for type inference in the resolved route
rawLocation: RouteLocationRaw,
currentLocation?: RouteLocationNormalizedLoaded
- ): RouteLocation & { href: string } {
+ ): RouteLocationResolved {
+ // const resolve: Router['resolve'] = (rawLocation: RouteLocationRaw, currentLocation) => {
// const objectLocation = routerLocationAsObject(rawLocation)
// we create a copy to modify it later
currentLocation = assign({}, currentLocation || currentRoute.value)
}
function pushWithRedirect(
- to: RouteLocationRaw | RouteLocation,
+ to: RouteLocationRaw,
redirectedFrom?: RouteLocation
): Promise<NavigationFailure | void | undefined> {
const targetLocation: RouteLocation = (pendingLocation = resolve(to))
removeRoute,
hasRoute,
getRoutes,
+ // @ts-expect-error: FIXME: types do not match
resolve,
options,
} from './route-location'
export type { RouteRecordRedirectOption } from './route-records'
+
+export type {
+ NavigationGuard,
+ NavigationGuardReturn,
+ NavigationHookAfter,
+ NavigationGuardWithThis,
+} from './navigation-guards'
--- /dev/null
+import type { _Awaitable } from '../types/utils'
+import type { NavigationGuardNext } from '../types'
+import type {
+ _RouteLocationNormalizedLoaded,
+ RouteLocationNormalizedTypedList,
+ RouteLocationNormalizedLoadedTypedList,
+ RouteLocationAsString,
+ RouteLocationAsRelativeTypedList,
+ RouteLocationAsPathTypedList,
+ _RouteLocationNormalized,
+} from './route-location'
+import type { _RouteMapGeneric, RouteMap } from './route-map'
+import type { NavigationFailure } from '../errors'
+
+/**
+ * Return types for a Navigation Guard. Accepts a type param for the RouteMap.
+ */
+type NavigationGuardReturnTyped<RouteMap extends _RouteMapGeneric> =
+ | void
+ | Error
+ | boolean
+ | RouteLocationAsString<RouteMap>
+ | RouteLocationAsRelativeTypedList<RouteMap>[keyof RouteMap]
+ | RouteLocationAsPathTypedList<RouteMap>[keyof RouteMap]
+
+/**
+ * Return types for a Navigation Guard. Based on `TypesConfig`
+ *
+ * @see {@link TypesConfig}
+ * @see {@link NavigationGuardReturnTyped}
+ */
+export type NavigationGuardReturn = NavigationGuardReturnTyped<RouteMap>
+
+/**
+ * Typed Navigation Guard with a type parameter for `this` and another for the route map.
+ */
+export interface NavigationGuardWithThisTyped<
+ T,
+ RouteMap extends _RouteMapGeneric
+> {
+ (
+ this: T,
+ to: RouteLocationNormalizedTypedList<RouteMap>[keyof RouteMap],
+ from: RouteLocationNormalizedLoadedTypedList<RouteMap>[keyof RouteMap],
+ // intentionally not typed to make people use the return
+ next: NavigationGuardNext
+ ): _Awaitable<NavigationGuardReturnTyped<RouteMap>>
+}
+
+/**
+ * Typed Navigation Guard with a type parameter for `this`. Based on `TypesConfig`
+ * @see {@link TypesConfig}
+ * @see {@link NavigationGuardWithThisTyped}
+ */
+export interface NavigationGuardWithThis<T>
+ extends NavigationGuardWithThisTyped<T, RouteMap> {}
+
+/**
+ * In `router.beforeResolve((to) => {})`, the `to` is typed as `RouteLocationNormalizedLoaded`, not
+ * `RouteLocationNormalized` like in `router.beforeEach()`. In practice it doesn't change much as users do not rely on
+ * the difference between them but if we update the type in vue-router, we will have to update this type too.
+ * @internal
+ */
+export interface _NavigationGuardResolved {
+ (
+ this: undefined,
+ to: _RouteLocationNormalizedLoaded,
+ from: _RouteLocationNormalizedLoaded,
+ // intentionally not typed to make people use the return
+ next: NavigationGuardNext
+ ): _Awaitable<NavigationGuardReturn>
+}
+
+/**
+ * Typed Navigation Guard. Accepts a type param for the RouteMap.
+ */
+export interface NavigationGuardTyped<RouteMap extends _RouteMapGeneric> {
+ (
+ to: _RouteLocationNormalized,
+ from: _RouteLocationNormalizedLoaded,
+ // intentionally not typed to make people use the return
+ next: NavigationGuardNext
+ ): _Awaitable<NavigationGuardReturnTyped<RouteMap>>
+}
+
+/**
+ * Typed Navigation Guard. Based on `TypesConfig`.
+ * @see {@link TypesConfig}
+ * @see {@link NavigationGuardWithThisTyped}
+ */
+export type NavigationGuard = NavigationGuardTyped<RouteMap>
+
+/**
+ * Typed Navigation Hook After. Accepts a type param for the RouteMap.
+ */
+export interface NavigationHookAfterTyped<RouteMap extends _RouteMapGeneric> {
+ (
+ to: RouteLocationNormalizedTypedList<RouteMap>[keyof RouteMap],
+ from: RouteLocationNormalizedLoadedTypedList<RouteMap>[keyof RouteMap],
+ failure?: NavigationFailure | void
+ ): unknown
+}
+
+/**
+ * Typed Navigation Hook After. Based on `TypesConfig`.
+ * @see {@link TypesConfig}
+ * @see {@link NavigationHookAfterTyped}
+ */
+export interface NavigationHookAfter
+ extends NavigationHookAfterTyped<RouteMap> {}
-// TODO: refactor to ParamValueRaw and ParamValue ?
-
/**
* Utility type for raw and non raw params like :id+
*
export type ParamValue<isRaw extends boolean> = true extends isRaw
? string | number
: string
+
+// TODO: finish this refactor
+// export type ParamValueOneOrMoreRaw = [ParamValueRaw, ...ParamValueRaw[]]
+// export type ParamValue = string
+// export type ParamValueRaw = string | number
import { RouteRecord, RouteRecordNormalized } from '../matcher/types'
import { HistoryState } from '../history/common'
import { NavigationFailure } from '../errors'
-import { RouteRecordRedirectOption } from '../typed-routes'
+import {
+ NavigationGuardWithThis,
+ RouteRecordRedirectOption,
+} from '../typed-routes'
+import { _Awaitable } from './utils'
export type Lazy<T> = () => Promise<T>
export type Override<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U
MatcherLocationAsPath,
RouteLocationOptions {}
+// TODO: rename in next major
export interface RouteLocationMatched extends RouteRecordNormalized {
// components cannot be Lazy<RouteComponent>
components: Record<string, RouteComponent> | null | undefined
(
// TODO: we could maybe add extra information like replace: true/false
to: RouteLocationNormalized,
- from: RouteLocationNormalized,
+ from: RouteLocationNormalizedLoaded,
next: NavigationGuardNext
// FIXME: this one shouldn't allow returning () => ...
- ): NavigationGuardReturn | Promise<NavigationGuardReturn>
-}
-
-/**
- * {@inheritDoc NavigationGuard}
- */
-export interface NavigationGuardWithThis<T> {
- (
- this: T,
- to: RouteLocationNormalized,
- from: RouteLocationNormalized,
- next: NavigationGuardNext
- ): NavigationGuardReturn | Promise<NavigationGuardReturn>
+ ): _Awaitable<NavigationGuardReturn>
}
export interface NavigationHookAfter {
-import { RouteLocationRaw, RouteRecordName } from '../typed-routes'
+import type { RouteLocationRaw } from '../typed-routes'
export function isRouteLocation(route: any): route is RouteLocationRaw {
return typeof route === 'string' || (route && typeof route === 'object')
import { routerKey, routeLocationKey } from './injectionSymbols'
import { Router } from './router'
import { RouteMap } from './typed-routes/route-map'
-import { RouteLocationNormalized } from './typed-routes'
+import { RouteLocationNormalizedLoaded } from './typed-routes'
/**
* Returns the router instance. Equivalent to using `$router` inside
*/
export function useRoute<Name extends keyof RouteMap = keyof RouteMap>(
_name?: Name
-): RouteLocationNormalized<Name> {
+): RouteLocationNormalizedLoaded<Name> {
+ // @ts-expect-error: FIXME: name mismatch issue
return inject(routeLocationKey)!
}