From: Eduardo San Martin Morote Date: Mon, 10 Jun 2024 14:40:53 +0000 (+0200) Subject: refactor: migrate guard types X-Git-Tag: v4.4.0-alpha.0~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=51edf6924dfc4b7f4477ed7dce868742d94e3aa5;p=thirdparty%2Fvuejs%2Frouter.git refactor: migrate guard types --- diff --git a/packages/router/__tests__/errors.spec.ts b/packages/router/__tests__/errors.spec.ts index c176832e..952059ad 100644 --- a/packages/router/__tests__/errors.spec.ts +++ b/packages/router/__tests__/errors.spec.ts @@ -8,7 +8,7 @@ import { 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, diff --git a/packages/router/__tests__/guards/beforeRouteEnter.spec.ts b/packages/router/__tests__/guards/beforeRouteEnter.spec.ts index 82e05418..0af22f07 100644 --- a/packages/router/__tests__/guards/beforeRouteEnter.spec.ts +++ b/packages/router/__tests__/guards/beforeRouteEnter.spec.ts @@ -1,6 +1,6 @@ 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: `
Home
` } const Foo = { template: `
Foo
` } diff --git a/packages/router/__tests__/guards/extractComponentsGuards.spec.ts b/packages/router/__tests__/guards/extractComponentsGuards.spec.ts index a8bfb4d8..dee5c758 100644 --- a/packages/router/__tests__/guards/extractComponentsGuards.spec.ts +++ b/packages/router/__tests__/guards/extractComponentsGuards.spec.ts @@ -1,9 +1,8 @@ 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() diff --git a/packages/router/__tests__/utils.ts b/packages/router/__tests__/utils.ts index 620757ce..62f258b1 100644 --- a/packages/router/__tests__/utils.ts +++ b/packages/router/__tests__/utils.ts @@ -1,9 +1,7 @@ import { JSDOM, ConstructorOptions } from 'jsdom' import { - NavigationGuard, RouteRecordMultipleViews, MatcherLocation, - RouteLocationNormalized, RouteComponent, RouteRecordRaw, RouteRecordName, @@ -17,6 +15,8 @@ import { Router, RouterView, RouteRecordNormalized, + NavigationGuard, + RouteLocationNormalized, } from '../src' export const tick = (time?: number) => diff --git a/packages/router/src/RouterLink.ts b/packages/router/src/RouterLink.ts index d2e66fa2..6db1f235 100644 --- a/packages/router/src/RouterLink.ts +++ b/packages/router/src/RouterLink.ts @@ -26,19 +26,18 @@ import { // @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 { /** @@ -82,6 +81,7 @@ export interface RouterLinkProps extends RouterLinkOptions { } export interface UseLinkDevtoolsContext { + // TODO: loaded type ? route: RouteLocationNormalized & { href: string } isActive: boolean isExactActive: boolean @@ -127,7 +127,10 @@ export function useLink(props: UseLinkOptions) { 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(() => { diff --git a/packages/router/src/RouterView.ts b/packages/router/src/RouterView.ts index ad4d8f72..33a09b50 100644 --- a/packages/router/src/RouterView.ts +++ b/packages/router/src/RouterView.ts @@ -17,11 +17,11 @@ import { VNode, Component, } from 'vue' -import { +import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, - RouteLocationMatched, -} from './types' +} from './typed-routes' +import type { RouteLocationMatched } from './types' import { matchedRouteKey, viewDepthKey, diff --git a/packages/router/src/globalExtensions.ts b/packages/router/src/globalExtensions.ts index 36897857..7aa983d6 100644 --- a/packages/router/src/globalExtensions.ts +++ b/packages/router/src/globalExtensions.ts @@ -2,9 +2,9 @@ import type { 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' diff --git a/packages/router/src/index.ts b/packages/router/src/index.ts index 140a7318..d93edc49 100644 --- a/packages/router/src/index.ts +++ b/packages/router/src/index.ts @@ -4,8 +4,6 @@ export { createWebHashHistory } from './history/hash' export { createRouterMatcher } from './matcher' export type { RouterMatcher } from './matcher' -export type * from './typed-routes' - export { parseQuery, stringifyQuery } from './query' export type { LocationQuery, @@ -38,21 +36,22 @@ export type { 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, @@ -62,11 +61,38 @@ export type { 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' diff --git a/packages/router/src/matcher/types.ts b/packages/router/src/matcher/types.ts index 6693a3d7..774c79d4 100644 --- a/packages/router/src/matcher/types.ts +++ b/packages/router/src/matcher/types.ts @@ -1,6 +1,6 @@ +import type { NavigationGuard } from '../typed-routes' import { RouteRecordMultipleViews, - NavigationGuard, _RouteRecordBase, _RouteRecordProps, NavigationGuardNextCallback, diff --git a/packages/router/src/navigationGuards.ts b/packages/router/src/navigationGuards.ts index 4bfd504b..2c981e1b 100644 --- a/packages/router/src/navigationGuards.ts +++ b/packages/router/src/navigationGuards.ts @@ -1,16 +1,19 @@ 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 { @@ -139,7 +142,12 @@ export function guardToPromiseFn( 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( diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index c7b1467f..3be4a7ff 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -1,10 +1,8 @@ import { RouteRecordRaw, - NavigationHookAfter, Lazy, isRouteLocation, isRouteName, - NavigationGuardWithThis, RouteLocationOptions, MatcherLocationRaw, } from './types' @@ -15,6 +13,9 @@ import type { RouteParams, RouteLocationNormalized, RouteLocationNormalizedLoaded, + NavigationGuardWithThis, + NavigationHookAfter, + RouteLocationResolved, } from './typed-routes' import { RouterHistory, HistoryState, NavigationType } from './history/common' import { @@ -63,6 +64,12 @@ 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 @@ -195,7 +202,7 @@ export interface Router { 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 @@ -238,10 +245,13 @@ export interface Router { * @param to - Raw route location to resolve * @param currentLocation - Optional current location to resolve against */ - resolve( - to: RouteLocationRaw, + resolve( + to: + | RouteLocationAsString + | RouteLocationAsRelativeTyped + | RouteLocationAsPathTyped, currentLocation?: RouteLocationNormalizedLoaded - ): RouteLocation & { href: string } + ): RouteLocationResolved /** * Programmatically navigate to a new URL by pushing an entry in the history @@ -435,9 +445,11 @@ export function createRouter(options: RouterOptions): Router { } 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) @@ -655,7 +667,7 @@ export function createRouter(options: RouterOptions): Router { } function pushWithRedirect( - to: RouteLocationRaw | RouteLocation, + to: RouteLocationRaw, redirectedFrom?: RouteLocation ): Promise { const targetLocation: RouteLocation = (pendingLocation = resolve(to)) @@ -1213,6 +1225,7 @@ export function createRouter(options: RouterOptions): Router { removeRoute, hasRoute, getRoutes, + // @ts-expect-error: FIXME: types do not match resolve, options, diff --git a/packages/router/src/typed-routes/index.ts b/packages/router/src/typed-routes/index.ts index 96fd6ac1..b88c0d4d 100644 --- a/packages/router/src/typed-routes/index.ts +++ b/packages/router/src/typed-routes/index.ts @@ -20,3 +20,10 @@ export type { } from './route-location' export type { RouteRecordRedirectOption } from './route-records' + +export type { + NavigationGuard, + NavigationGuardReturn, + NavigationHookAfter, + NavigationGuardWithThis, +} from './navigation-guards' diff --git a/packages/router/src/typed-routes/navigation-guards.ts b/packages/router/src/typed-routes/navigation-guards.ts new file mode 100644 index 00000000..b4dac11b --- /dev/null +++ b/packages/router/src/typed-routes/navigation-guards.ts @@ -0,0 +1,110 @@ +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 = + | void + | Error + | boolean + | RouteLocationAsString + | RouteLocationAsRelativeTypedList[keyof RouteMap] + | RouteLocationAsPathTypedList[keyof RouteMap] + +/** + * Return types for a Navigation Guard. Based on `TypesConfig` + * + * @see {@link TypesConfig} + * @see {@link NavigationGuardReturnTyped} + */ +export type NavigationGuardReturn = NavigationGuardReturnTyped + +/** + * 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[keyof RouteMap], + from: RouteLocationNormalizedLoadedTypedList[keyof RouteMap], + // intentionally not typed to make people use the return + next: NavigationGuardNext + ): _Awaitable> +} + +/** + * Typed Navigation Guard with a type parameter for `this`. Based on `TypesConfig` + * @see {@link TypesConfig} + * @see {@link NavigationGuardWithThisTyped} + */ +export interface NavigationGuardWithThis + extends NavigationGuardWithThisTyped {} + +/** + * 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 +} + +/** + * Typed Navigation Guard. Accepts a type param for the RouteMap. + */ +export interface NavigationGuardTyped { + ( + to: _RouteLocationNormalized, + from: _RouteLocationNormalizedLoaded, + // intentionally not typed to make people use the return + next: NavigationGuardNext + ): _Awaitable> +} + +/** + * Typed Navigation Guard. Based on `TypesConfig`. + * @see {@link TypesConfig} + * @see {@link NavigationGuardWithThisTyped} + */ +export type NavigationGuard = NavigationGuardTyped + +/** + * Typed Navigation Hook After. Accepts a type param for the RouteMap. + */ +export interface NavigationHookAfterTyped { + ( + to: RouteLocationNormalizedTypedList[keyof RouteMap], + from: RouteLocationNormalizedLoadedTypedList[keyof RouteMap], + failure?: NavigationFailure | void + ): unknown +} + +/** + * Typed Navigation Hook After. Based on `TypesConfig`. + * @see {@link TypesConfig} + * @see {@link NavigationHookAfterTyped} + */ +export interface NavigationHookAfter + extends NavigationHookAfterTyped {} diff --git a/packages/router/src/typed-routes/params.ts b/packages/router/src/typed-routes/params.ts index 15b04388..4192ed45 100644 --- a/packages/router/src/typed-routes/params.ts +++ b/packages/router/src/typed-routes/params.ts @@ -1,5 +1,3 @@ -// TODO: refactor to ParamValueRaw and ParamValue ? - /** * Utility type for raw and non raw params like :id+ * @@ -32,3 +30,8 @@ export type ParamValueZeroOrOne = true extends isRaw export type ParamValue = true extends isRaw ? string | number : string + +// TODO: finish this refactor +// export type ParamValueOneOrMoreRaw = [ParamValueRaw, ...ParamValueRaw[]] +// export type ParamValue = string +// export type ParamValueRaw = string | number diff --git a/packages/router/src/types/index.ts b/packages/router/src/types/index.ts index 8b1d2be6..9bcc131d 100644 --- a/packages/router/src/types/index.ts +++ b/packages/router/src/types/index.ts @@ -4,7 +4,11 @@ import { Ref, ComponentPublicInstance, Component, DefineComponent } from 'vue' 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 = () => Promise export type Override = Pick> & U @@ -142,6 +146,7 @@ export interface RouteLocationPathRaw MatcherLocationAsPath, RouteLocationOptions {} +// TODO: rename in next major export interface RouteLocationMatched extends RouteRecordNormalized { // components cannot be Lazy components: Record | null | undefined @@ -484,22 +489,10 @@ export interface NavigationGuard { ( // 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 -} - -/** - * {@inheritDoc NavigationGuard} - */ -export interface NavigationGuardWithThis { - ( - this: T, - to: RouteLocationNormalized, - from: RouteLocationNormalized, - next: NavigationGuardNext - ): NavigationGuardReturn | Promise + ): _Awaitable } export interface NavigationHookAfter { diff --git a/packages/router/src/types/typeGuards.ts b/packages/router/src/types/typeGuards.ts index 7d1f06ba..97d55281 100644 --- a/packages/router/src/types/typeGuards.ts +++ b/packages/router/src/types/typeGuards.ts @@ -1,4 +1,4 @@ -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') diff --git a/packages/router/src/useApi.ts b/packages/router/src/useApi.ts index 853100b6..e473c87b 100644 --- a/packages/router/src/useApi.ts +++ b/packages/router/src/useApi.ts @@ -2,7 +2,7 @@ import { inject } from 'vue' 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 @@ -18,6 +18,7 @@ export function useRouter(): Router { */ export function useRoute( _name?: Name -): RouteLocationNormalized { +): RouteLocationNormalizedLoaded { + // @ts-expect-error: FIXME: name mismatch issue return inject(routeLocationKey)! }