]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
refactor: revert the complex types for path parsing
authorEduardo San Martin Morote <posva13@gmail.com>
Wed, 29 Jun 2022 12:41:11 +0000 (14:41 +0200)
committerEduardo San Martin Morote <posva@users.noreply.github.com>
Thu, 30 Jun 2022 07:59:00 +0000 (09:59 +0200)
Achieve it with a build plugin instead; https://github.com/posva/unplugin-vue-router
Types were too slow due to their recursive nature and relying on tuples
too much. A build-time type generation is not only reliable and fast but
also enable creating other patterns.

13 files changed:
packages/router/src/RouterLink.ts
packages/router/src/globalExtensions.ts
packages/router/src/index.ts
packages/router/src/injectionSymbols.ts
packages/router/src/router.ts
packages/router/src/typedRouter.ts [deleted file]
packages/router/src/types/index.ts
packages/router/src/types/named.ts [deleted file]
packages/router/src/types/paths.ts [deleted file]
packages/router/src/useApi.ts
packages/router/test-dts/components.test-d.tsx
packages/router/test-dts/namedRoutes.test-d.ts [deleted file]
packages/router/test-dts/paths.test-d.ts [deleted file]

index e54aa0fa0569572265f9d4b0f9c3cc91289b1f79..9bd9568f1c2d2a2cd42c559484ffcd60523560b6 100644 (file)
@@ -31,26 +31,18 @@ import {
   VueUseOptions,
   RouteLocation,
   RouteLocationNormalized,
-  RouteLocationPathRaw,
-  RouteLocationString,
-  RouteLocationNamedRaw,
 } 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 type { Router } from './router'
-import type { RouteNamedMap, RouteStaticPathMap } from './types/named'
-import type { RouterTyped } from './typedRouter'
 
-export interface RouterLinkOptions<
-  Routes extends RouteLocationRaw = RouteLocationRaw
-> {
+export interface RouterLinkOptions {
   /**
    * Route Location the link should navigate to when clicked on.
    */
-  to: Routes
+  to: RouteLocationRaw
   /**
    * Calls `router.replace` instead of `router.push`.
    */
@@ -58,9 +50,7 @@ export interface RouterLinkOptions<
   // TODO: refactor using extra options allowed in router.push. Needs RFC
 }
 
-export interface RouterLinkProps<
-  Routes extends RouteLocationRaw = RouteLocationRaw
-> extends RouterLinkOptions<Routes> {
+export interface RouterLinkProps extends RouterLinkOptions {
   /**
    * Whether RouterLink should not wrap its content in an `a` tag. Useful when
    * using `v-slot` to create a custom RouterLink
@@ -261,22 +251,20 @@ export const RouterLinkImpl = /*#__PURE__*/ defineComponent({
 /**
  * Component to render a link that triggers a navigation on click.
  */
-export const RouterLink: RouterLinkTyped = RouterLinkImpl as any
+export const RouterLink: _RouterLinkI = RouterLinkImpl as any
 
 /**
  * Typed version of the `RouterLink` component. Its generic defaults to the typed router so it can be inferred
  * automatically for JSX.
+ *
+ * @internal
  */
-export interface RouterLinkTyped<R extends Router = RouterTyped> {
+export interface _RouterLinkI {
   new (): {
     $props: AllowedComponentProps &
       ComponentCustomProps &
       VNodeProps &
-      RouterLinkProps<
-        | RouteLocationNamedRaw<RouteNamedMap<R['options']['routes']>>
-        | RouteLocationString<RouteStaticPathMap<R['options']['routes']>>
-        | RouteLocationPathRaw<RouteStaticPathMap<R['options']['routes']>>
-      >
+      RouterLinkProps
 
     $slots: {
       default: (arg: UnwrapRef<ReturnType<typeof useLink>>) => VNode[]
index 878ec323ca514e1c20a27f14d474f2131206b8d0..1a940463444987bef85a57537bffd30e372a0212 100644 (file)
@@ -4,8 +4,8 @@ import type {
   RouteLocationNormalizedLoaded,
 } from './types'
 import { RouterView } from './RouterView'
-import type { RouterLinkTyped } from './RouterLink'
-import type { RouterTyped } from './typedRouter'
+import { RouterLink } from './RouterLink'
+import type { Router } from './router'
 
 declare module '@vue/runtime-core' {
   export interface ComponentCustomOptions {
@@ -55,11 +55,11 @@ declare module '@vue/runtime-core' {
     /**
      * {@link Router} instance used by the application.
      */
-    $router: RouterTyped
+    $router: Router
   }
 
   export interface GlobalComponents {
     RouterView: typeof RouterView
-    RouterLink: RouterLinkTyped<RouterTyped>
+    RouterLink: typeof RouterLink
   }
 }
index c93fe522b28293182e22f34c49c7aa66a4f20968..3a5f738a7f98af7d6f4c01815f4197e86486d56d 100644 (file)
@@ -46,7 +46,6 @@ export type {
   RouteParamValueRaw,
   RouteLocationNamedRaw,
   RouteLocationPathRaw,
-  RouteLocationString,
   RouteLocationMatched,
   RouteLocationOptions,
   RouteRecordRedirectOption,
@@ -62,26 +61,6 @@ export type {
   NavigationGuardWithThis,
   NavigationHookAfter,
 } from './types'
-export type {
-  ParamsFromPath,
-  ParamsRawFromPath,
-  _StripRegex,
-  _RemoveUntilClosingPar,
-  _ExtractParamsOfPath,
-  _ParamExtractResult,
-  _ExtractModifier,
-  _ModifierExtracTResult,
-  _JoinPath,
-  _ParamDelimiter,
-  _ParamModifier,
-} from './types/paths'
-export type {
-  RouteNamedMap,
-  RouteStaticPathMap,
-  RouteNamedInfo,
-  _RouteRecordNamedBaseInfo,
-} from './types/named'
-export type { Config, RouterTyped } from './typedRouter'
 
 export { createRouter } from './router'
 export type { Router, RouterOptions, RouterScrollBehavior } from './router'
@@ -100,9 +79,9 @@ export {
 } from './navigationGuards'
 export { RouterLink, useLink } from './RouterLink'
 export type {
+  _RouterLinkI,
   RouterLinkProps,
   UseLinkOptions,
-  RouterLinkTyped,
 } from './RouterLink'
 export { RouterView } from './RouterView'
 export type { RouterViewProps } from './RouterView'
index e04ef294f945bfba59259f9aa670b584431c15d7..ad391624f077c2570ac43f35e685b16009d55798 100644 (file)
@@ -1,7 +1,7 @@
-import { InjectionKey, ComputedRef, Ref } from 'vue'
+import type { InjectionKey, ComputedRef, Ref } from 'vue'
 import { RouteLocationNormalizedLoaded } from './types'
 import { RouteRecordNormalized } from './matcher/types'
-import { RouterTyped } from './typedRouter'
+import type { Router } from './router'
 
 /**
  * RouteRecord being rendered by the closest ancestor Router View. Used for
@@ -30,9 +30,7 @@ export const viewDepthKey = Symbol(
  *
  * @internal
  */
-export const routerKey = Symbol(
-  __DEV__ ? 'router' : ''
-) as InjectionKey<RouterTyped>
+export const routerKey = Symbol(__DEV__ ? 'router' : '') as InjectionKey<Router>
 
 /**
  * Allows overriding the current route returned by `useRoute` in tests. rl
index 177693cc55c8812d88a6ec787095e4fcda2e7b55..e44946c23897df1888ce86131f1e6d1af5487a89 100644 (file)
@@ -13,9 +13,6 @@ import {
   RouteLocationOptions,
   MatcherLocationRaw,
   RouteParams,
-  RouteLocationNamedRaw,
-  RouteLocationPathRaw,
-  RouteLocationString,
 } from './types'
 import { RouterHistory, HistoryState, NavigationType } from './history/common'
 import {
@@ -71,7 +68,6 @@ import {
   routerViewLocationKey,
 } from './injectionSymbols'
 import { addDevtools } from './devtools'
-import { RouteNamedMap, RouteStaticPathMap } from './types/named'
 
 /**
  * Internal type to define an ErrorHandler
@@ -185,9 +181,9 @@ export interface RouterOptions extends PathParserOptions {
 }
 
 /**
- * Router instance. **The `Options` generic is internal**.
+ * Router instance.
  */
-export interface Router<Options extends RouterOptions = RouterOptions> {
+export interface Router {
   /**
    * @internal
    */
@@ -199,7 +195,7 @@ export interface Router<Options extends RouterOptions = RouterOptions> {
   /**
    * Original options object passed to create the Router
    */
-  readonly options: Options
+  readonly options: RouterOptions
 
   /**
    * Allows turning off the listening of history events. This is a low level api for micro-frontends.
@@ -256,12 +252,7 @@ export interface Router<Options extends RouterOptions = RouterOptions> {
    *
    * @param to - Route location to navigate to
    */
-  push(
-    to:
-      | RouteLocationNamedRaw<RouteNamedMap<Options['routes']>>
-      | RouteLocationString<RouteStaticPathMap<Options['routes']>>
-      | RouteLocationPathRaw<RouteStaticPathMap<Options['routes']>>
-  ): Promise<NavigationFailure | void | undefined>
+  push(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>
 
   /**
    * Programmatically navigate to a new URL by replacing the current entry in
@@ -269,12 +260,7 @@ export interface Router<Options extends RouterOptions = RouterOptions> {
    *
    * @param to - Route location to navigate to
    */
-  replace(
-    to:
-      | RouteLocationNamedRaw<RouteNamedMap<Options['routes']>>
-      | RouteLocationString<RouteStaticPathMap<Options['routes']>>
-      | RouteLocationPathRaw<RouteStaticPathMap<Options['routes']>>
-  ): Promise<NavigationFailure | void | undefined>
+  replace(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>
 
   /**
    * Go back in history if possible by calling `history.back()`. Equivalent to
@@ -373,9 +359,7 @@ export interface Router<Options extends RouterOptions = RouterOptions> {
  *
  * @param options - {@link RouterOptions}
  */
-export function createRouter<Options extends RouterOptions>(
-  options: Options
-): Router<Options> {
+export function createRouter(options: RouterOptions): Router {
   const matcher = createRouterMatcher(options.routes, options)
   const parseQuery = options.parseQuery || originalParseQuery
   const stringifyQuery = options.stringifyQuery || originalStringifyQuery
@@ -1173,7 +1157,7 @@ export function createRouter<Options extends RouterOptions>(
   let started: boolean | undefined
   const installedApps = new Set<App>()
 
-  const router: Router<Options> = {
+  const router: Router = {
     currentRoute,
     listening: true,
 
diff --git a/packages/router/src/typedRouter.ts b/packages/router/src/typedRouter.ts
deleted file mode 100644 (file)
index d5cf4bc..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { Router } from './router'
-
-/**
- * Vue Router Configuration that allows to add global types for better type support.
- *
- * @example
- *
- * ```ts
- * const router = createRouter({
- *   // ...
- *   routes: [
- *     // ...
- *   ] as const // IMPORTANT
- * })
- *
- * declare module 'vue-router' {
- *   export interface Config {
- *     // allow global functions to get a typed router
- *     Router: typeof router
- *   }
- * }
- * ```
- */
-export interface Config {
-  // Router: unknown
-}
-
-export type RouterTyped = Config extends Record<'Router', infer R> ? R : Router
index 851b4d8aa91f4961a43b0c75afd56d5c2f1be52b..b693aa84a75aaf56628e4b048c2aac10e66d8adc 100644 (file)
@@ -4,12 +4,6 @@ import { Ref, ComponentPublicInstance, Component, DefineComponent } from 'vue'
 import { RouteRecord, RouteRecordNormalized } from '../matcher/types'
 import { HistoryState } from '../history/common'
 import { NavigationFailure } from '../errors'
-import {
-  RouteNamedInfo,
-  RouteNamedMapGeneric,
-  RouteStaticPathMapGeneric,
-} from './named'
-import { LiteralUnion } from './utils'
 
 export type Lazy<T> = () => Promise<T>
 export type Override<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U
@@ -73,25 +67,16 @@ export interface MatcherLocationAsName {
 /**
  * @internal
  */
-export interface LocationAsRelativeRaw<
-  Name extends RouteRecordName = RouteRecordName,
-  Info extends RouteNamedInfo = RouteNamedInfo
-> {
-  name?: Name
-  params?: Info extends RouteNamedInfo<any, any, infer ParamsRaw>
-    ? ParamsRaw
-    : RouteParamsRaw
+export interface LocationAsRelativeRaw {
+  name?: string
+  params?: RouteParamsRaw
 }
 
 /**
  * @internal
  */
-export interface MatcherLocationAsRelative<
-  Info extends RouteNamedInfo = RouteNamedInfo
-> {
-  params?: Info extends RouteNamedInfo<any, infer Params, any>
-    ? Params
-    : RouteParams
+export interface MatcherLocationAsRelative {
+  params?: RouteParams
 }
 
 /**
@@ -124,59 +109,25 @@ export type RouteLocationRaw =
   | RouteLocationPathRaw
   | RouteLocationNamedRaw
 
-/**
- * Route location that can infer full path locations
- *
- * @internal
- */
-export type RouteLocationString<
-  RouteMap extends RouteStaticPathMapGeneric = RouteStaticPathMapGeneric
-> = RouteStaticPathMapGeneric extends RouteMap
-  ? string
-  : LiteralUnion<
-      {
-        [K in keyof RouteMap]: RouteMap[K]
-      }[keyof RouteMap],
-      string
-    >
-
 /**
  * Route Location that can infer the necessary params based on the name.
  *
  * @internal
  */
-export type RouteLocationNamedRaw<
-  RouteMap extends RouteNamedMapGeneric = RouteNamedMapGeneric
-> = RouteNamedMapGeneric extends RouteMap
-  ? // allows assigning a RouteLocationRaw to RouteLocationNamedRaw
-    RouteQueryAndHash & LocationAsRelativeRaw & RouteLocationOptions
-  : {
-      [K in Extract<keyof RouteMap, RouteRecordName>]: LocationAsRelativeRaw<
-        K,
-        RouteMap[K]
-      >
-    }[Extract<keyof RouteMap, RouteRecordName>] &
-      RouteQueryAndHash &
-      RouteLocationOptions
+export interface RouteLocationNamedRaw
+  extends RouteQueryAndHash,
+    LocationAsRelativeRaw,
+    RouteLocationOptions {}
 
 /**
  * Route Location that can infer the possible paths.
  *
  * @internal
  */
-export type RouteLocationPathRaw<
-  RouteMap extends RouteStaticPathMapGeneric = RouteStaticPathMapGeneric
-> = RouteStaticPathMapGeneric extends RouteMap
-  ? // allows assigning a RouteLocationRaw to RouteLocationPath
-    RouteQueryAndHash & MatcherLocationAsPath & RouteLocationOptions
-  : RouteQueryAndHash &
-      RouteLocationOptions &
-      MatcherLocationAsPath<
-        LiteralUnion<
-          { [K in keyof RouteMap]: RouteMap[K] }[keyof RouteMap],
-          string
-        >
-      >
+export interface RouteLocationPathRaw
+  extends RouteQueryAndHash,
+    MatcherLocationAsPath,
+    RouteLocationOptions {}
 
 export interface RouteLocationMatched extends RouteRecordNormalized {
   // components cannot be Lazy<RouteComponent>
diff --git a/packages/router/src/types/named.ts b/packages/router/src/types/named.ts
deleted file mode 100644 (file)
index d76ab30..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-import type {
-  RouteParams,
-  RouteParamsRaw,
-  RouteRecordRaw,
-  RouteRecordName,
-} from '.'
-import type { _JoinPath, ParamsFromPath, ParamsRawFromPath } from './paths'
-
-/**
- * Creates a map with each named route as a properties. Each property contains the type of the params in raw and
- * normalized versions as well as the raw path.
- * @internal
- */
-export type RouteNamedMap<
-  Routes extends Readonly<RouteRecordRaw[]>,
-  Prefix extends string = ''
-> = Routes extends readonly [infer R, ...infer Rest]
-  ? Rest extends Readonly<RouteRecordRaw[]>
-    ? (R extends _RouteRecordNamedBaseInfo<
-        infer Name,
-        infer Path,
-        infer Children
-      >
-        ? (Name extends RouteRecordName
-            ? {
-                [N in Name]: {
-                  // name: N
-                  params: ParamsFromPath<_JoinPath<Prefix, Path>>
-                  paramsRaw: ParamsRawFromPath<_JoinPath<Prefix, Path>>
-                  path: _JoinPath<Prefix, Path>
-                }
-              }
-            : {
-                // NO_NAME: 1
-              }) &
-            // Recurse children
-            (Children extends Readonly<RouteRecordRaw[]>
-              ? RouteNamedMap<Children, _JoinPath<Prefix, Path>>
-              : {
-                  // NO_CHILDREN: 1
-                })
-        : {
-            // EMPTY: 1
-          }) &
-        RouteNamedMap<Rest, Prefix>
-    : never // R must be a valid route record
-  : {
-      // END: 1
-    }
-
-/**
- * @internal
- */
-export type RouteStaticPathMap<
-  Routes extends Readonly<RouteRecordRaw[]>,
-  Prefix extends string = ''
-> = Routes extends readonly [infer R, ...infer Rest]
-  ? Rest extends Readonly<RouteRecordRaw[]>
-    ? (R extends _RouteRecordNamedBaseInfo<
-        infer _Name,
-        infer Path,
-        infer Children
-      >
-        ? {
-            [P in Path as _JoinPath<Prefix, Path>]: _JoinPath<Prefix, Path>
-          } & (Children extends Readonly<RouteRecordRaw[]> // Recurse children
-            ? RouteStaticPathMap<Children, _JoinPath<Prefix, Path>>
-            : {
-                // NO_CHILDREN: 1
-              })
-        : never) & // R must be a valid route record
-        // recurse children
-        RouteStaticPathMap<Rest, Prefix>
-    : {
-        // EMPTY: 1
-      }
-  : {
-      // END: 1
-    }
-
-/**
- * Important information in a Named Route Record
- * @internal
- */
-export interface _RouteRecordNamedBaseInfo<
-  Name extends RouteRecordName = RouteRecordName, // we don't care about symbols
-  Path extends string = string,
-  Children extends Readonly<RouteRecordRaw[]> = Readonly<RouteRecordRaw[]>
-> {
-  name?: Name
-  path: Path
-  children?: Children
-}
-
-/**
- * Generic map of named routes from a list of route records.
- *
- * @internal
- */
-export type RouteNamedMapGeneric = Record<RouteRecordName, RouteNamedInfo>
-
-/**
- * Generic map of routes paths from a list of route records.
- *
- * @internal
- */
-export type RouteStaticPathMapGeneric = Record<string, string>
-
-/**
- * Relevant information about a named route record to deduce its params.
- *
- * @internal
- */
-export interface RouteNamedInfo<
-  Path extends string = string,
-  Params extends RouteParams = RouteParams,
-  ParamsRaw extends RouteParamsRaw = RouteParamsRaw
-> {
-  params: Params
-  paramsRaw: ParamsRaw
-  path: Path
-}
diff --git a/packages/router/src/types/paths.ts b/packages/router/src/types/paths.ts
deleted file mode 100644 (file)
index 2f93e68..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-import { RouteParamValueRaw } from '.'
-import { Simplify, _AlphaNumeric } from './utils'
-
-/**
- * Extract an object of params given a path like `/users/:id`.
- *
- * @example
- * ```ts
- * type P = ParamsFromPath<'/:id/b/:c*'> // { id: string; c?: string[] }
- * ```
- */
-export type ParamsFromPath<P extends string = string> =
-  P extends `${string}:${string}`
-    ? Simplify<_ExtractParamsOfPath<P, false>>
-    : Record<any, never>
-
-export type ParamsRawFromPath<P extends string = string> =
-  P extends `${string}:${string}`
-    ? Simplify<_ExtractParamsOfPath<P, true>>
-    : Record<any, never>
-
-/**
- * Possible param modifiers.
- *
- * @internal
- */
-export type _ParamModifier = '+' | '?' | '*'
-
-/**
- * Characters that mark the end of a param. In reality, there is a lot more than
- * this as only alphanumerical + _ are accepted as params but that is impossible
- * to achieve with TS and in practice, This set should cover them all. TODO: Add
- * missing characters that do not need to be encoded.
- *
- * @internal
- */
-export type _ParamDelimiter =
-  | '-'
-  | '/'
-  | '%'
-  | ':'
-  | '('
-  | '\\'
-  | ';'
-  | ','
-  | '&'
-  | '!'
-  | "'"
-  | '='
-  | '@'
-  | '['
-  | ']'
-  | '$'
-  | _ParamModifier
-
-/**
- * Given a path, extracts the possible params or \{\} when there are no params.
- *
- * @internal
- */
-export type _ExtractParamsOfPath<
-  P extends string,
-  isRaw extends boolean
-> = P extends `${string}:${infer HasParam}`
-  ? _ExtractParamName<HasParam> extends _ParamExtractResult<
-      infer ParamName,
-      infer Rest
-    >
-    ? // ParamName is delimited by something eg: /:id/b/:c
-      // let's first remove the regex if there is one then extract the modifier
-      _ExtractModifier<_StripRegex<Rest>> extends _ModifierExtracTResult<
-        infer Modifier,
-        infer Rest2
-      >
-      ? _ParamToObject<ParamName, Modifier, isRaw> &
-          _ExtractParamsOfPath<Rest2, isRaw>
-      : never // this should never happen as the modifier can be empty
-    : // Nothing after the param: /:id, we are done
-      _ParamToObject<HasParam, '', isRaw>
-  : {
-      // EMPTY: 1
-    }
-
-/**
- * Helper type to infer a param name extraction result
- * @internal
- */
-export interface _ParamExtractResult<P extends string, Rest extends string> {
-  param: P
-  rest: Rest
-}
-
-/**
- * Extracts the param name from a path. Requires to strip off the starting `:`
- *
- * @example
- * ```ts
- * _ExtractParamName<'id/:b'> // 'id'
- * ```
- *
- * @internal
- */
-type _ExtractParamName<
-  Tail extends string,
-  Head extends string = ''
-> = Tail extends `${_AlphaNumeric}${infer Rest}`
-  ? Tail extends `${infer C}${Rest}`
-    ? // Keep extracting other alphanumeric chars
-      _ExtractParamName<Rest, `${Head}${C}`>
-    : never // ERR
-  : // end the recursion
-    _ParamExtractResult<Head, Tail>
-
-/**
- * We consider a what comes after a param, e.g. For `/:id(\\d+)+/edit`, it would be `(\\d+)+/edit`. This should output
- * everything after the regex while handling escaped `)`: `+/edit`. Note this type should be used with a string that
- * starts with `(` as it will remove everything until the closing parenthesis `)`.
- *
- * @internal
- */
-export type _StripRegex<S extends string> =
-  // do we have an escaped closing parenthesis?
-  S extends `(${infer A}\\)${infer Rest}`
-    ? // the actual regexp finished before, A has no escaped )
-      A extends `${string})${infer Rest2}`
-      ? // Rebuild the rest
-        `${Rest2}\\)${Rest}` // job done
-      : // NOTE: expensive type when there are multiple custom regex. It's a good idea to avoid multiple custom regexs in paths. Only use custom regexs when necessary or cast the string type: `path: /:id(...)/rest` as '/:id/rest'
-        _RemoveUntilClosingPar<Rest> // we keep removing
-    : // simple case with no escaping
-    S extends `(${string})${infer Rest}`
-    ? // extract the modifier if there is one
-      Rest
-    : // nothing to remove
-      S
-
-/**
- * Helper type to infer a modifier extraction result.
- *
- * @internal
- */
-export interface _ModifierExtracTResult<
-  M extends _ParamModifier | '',
-  Rest extends string
-> {
-  modifier: M
-  rest: Rest
-}
-
-/**
- * Extracts the modifier and the rest of the path. This is meant to be used with everything after the param name, e.g.,
- * given a path of `/:paths(.+)+/end`, it should be given `+/end` and will split the path into `+` and `/end`.
- *
- * @internal
- */
-export type _ExtractModifier<P extends string> =
-  P extends `${_ParamModifier}${infer Rest}`
-    ? P extends `${infer M}${Rest}`
-      ? M extends _ParamModifier
-        ? _ModifierExtracTResult<M, Rest>
-        : // impossible case
-          never
-      : // impossible case
-        never
-    : // No modifier present
-      _ModifierExtracTResult<'', P>
-
-/**
- * Gets the possible type of a param based on its modifier M.
- *
- * @internal
- */
-export type _ModifierParamValue<
-  M extends _ParamModifier | '' = _ParamModifier | '',
-  isRaw extends boolean = false
-> = '' extends M
-  ? _ParamValue<isRaw>
-  : '+' extends M
-  ? _ParamValueOneOrMore<isRaw>
-  : '*' extends M
-  ? _ParamValueZeroOrMore<isRaw>
-  : '?' extends M
-  ? _ParamValueZeroOrOne<isRaw>
-  : never
-
-/**
- * Utility type for raw and non raw params like :id+
- *
- * @internal
- */
-export type _ParamValueOneOrMore<isRaw extends boolean> = true extends isRaw
-  ? readonly [string | number, ...(string | number)[]]
-  : readonly [string, ...string[]]
-
-/**
- * Utility type for raw and non raw params like :id*
- *
- * @internal
- */
-export type _ParamValueZeroOrMore<isRaw extends boolean> = true extends isRaw
-  ? readonly (string | number)[] | undefined | null
-  : readonly string[] | undefined | null
-
-/**
- * Utility type for raw and non raw params like :id?
- *
- * @internal
- */
-export type _ParamValueZeroOrOne<isRaw extends boolean> = true extends isRaw
-  ? RouteParamValueRaw
-  : string
-
-/**
- * Utility type for raw and non raw params like :id
- *
- * @internal
- */
-export type _ParamValue<isRaw extends boolean> = true extends isRaw
-  ? string | number
-  : string
-
-/**
- * Given a param name N and its modifier M, creates a param object for the pair.
- *
- * @internal
- */
-export type _ParamToObject<
-  N extends string,
-  M extends _ParamModifier | '',
-  isRaw extends boolean
-> = M extends '?' | '*'
-  ? {
-      [K in N]?: _ModifierParamValue<M, isRaw>
-    }
-  : {
-      [K in N]: _ModifierParamValue<M, isRaw>
-    }
-
-/**
- * Takes the custom regex (and everything after) of a param and strips it off. Must be called with a string that starts
- * with a `(` **after the parenthesis**.
- *
- * @example
- * - `\\d+(?:inner-group\\)-end)/:rest-of-url` becomes `/:rest-of-url`
- *
- * @internal
- */
-export type _RemoveUntilClosingPar<S extends string> =
-  // do we have an escaped closing parenthesis?
-  S extends `${infer A}\\)${infer Rest}`
-    ? // the actual regexp finished before, A has no escaped )
-      A extends `${string})${infer Rest2}`
-      ? `${Rest2}\\)${Rest}` // job done
-      : _RemoveUntilClosingPar<Rest> // we keep removing
-    : S extends `${string})${infer Rest}`
-    ? Rest
-    : never // nothing to remove, should not have been called, easier to spot bugs
-
-/**
- * Joins a prefix and a path putting a `/` between them when necessary
- *
- * @internal
- */
-export type _JoinPath<
-  Prefix extends string,
-  Path extends string
-> = Path extends `/${string}`
-  ? Path
-  : '' extends Prefix
-  ? never
-  : `${Prefix}${Prefix extends `${string}/` ? '' : '/'}${Path}`
index c62ed0c057665ac040a3b14d7065ca3bba97b28d..9bf578539353b0f461ee6916d9b700877044ab5a 100644 (file)
@@ -1,13 +1,13 @@
 import { inject } from 'vue'
 import { routerKey, routeLocationKey } from './injectionSymbols'
-import { RouterTyped } from './typedRouter'
+import { Router } from './router'
 import { RouteLocationNormalizedLoaded } from './types'
 
 /**
  * Returns the router instance. Equivalent to using `$router` inside
  * templates.
  */
-export function useRouter(): RouterTyped {
+export function useRouter(): Router {
   return inject(routerKey)!
 }
 
index 880c558615993ceb6208cb4d61568d52803f338a..a7c9528169eec97d2f551ef4ac4b30bb7c9b5271 100644 (file)
@@ -24,8 +24,6 @@ expectType<JSX.Element>(<RouterLink to="/foo" />)
 expectType<JSX.Element>(<RouterLink class="link" to="/foo" />)
 expectType<JSX.Element>(<RouterLink to={{ path: '/foo' }} />)
 expectType<JSX.Element>(<RouterLink to={{ path: '/foo' }} custom />)
-// @ts-expect-error: non existing name
-expectError(<RouterLink to={{ name: '' }} />)
 
 // RouterView
 expectType<JSX.Element>(<RouterView class="view" />)
diff --git a/packages/router/test-dts/namedRoutes.test-d.ts b/packages/router/test-dts/namedRoutes.test-d.ts
deleted file mode 100644 (file)
index c563b81..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-import {
-  createRouter,
-  createWebHistory,
-  RouteRecordRaw,
-  expectType,
-  RouteNamedMap,
-  RouteLocationRaw,
-  _JoinPath,
-  useRouter,
-} from './index'
-import { DefineComponent } from 'vue'
-
-declare const component: DefineComponent
-declare const components: { default: DefineComponent }
-
-const routeName = Symbol()
-
-const r2 = createRouter({
-  history: createWebHistory(),
-  routes: [
-    { path: '/', component },
-    { path: '/foo', component },
-    { path: '/users/:id', name: 'UserDetails', component },
-    { path: '/no-name', /* no name */ components },
-    {
-      path: '/nested',
-      name: 'nested',
-      children: [
-        {
-          path: ':a',
-          name: 'nested-a',
-          children: [
-            {
-              path: 'b',
-              children: [{ path: ':c', name: 'nested-c', component }],
-            },
-          ],
-        },
-        { path: ':opt?', name: 'optional', component },
-        // still skipped
-        { path: 'other', name: routeName, component },
-      ],
-    },
-  ] as const,
-})
-
-const methods = ['push', 'replace'] as const
-for (const method of methods) {
-  r2.push({ name: 'UserDetails' })
-  r2.replace({ name: 'UserDetails' })
-
-  // accepts missing params because of relative locations is valid
-  r2[method]({ name: 'UserDetails' })
-  // @ts-expect-error: but expects a correct id
-  r2[method]({ name: 'UserDetails', params: {} })
-  // @ts-expect-error: no invalid params
-  r2[method]({ name: 'UserDetails', params: { id: '2', nope: 'oops' } })
-  // other options are valid
-  r2[method]({ name: 'UserDetails', query: { valid: 'true' }, replace: true })
-  r2[method]({ name: 'UserDetails', params: { id: '2' } })
-  // accepts numbers
-  r2[method]({ name: 'UserDetails', params: { id: 2 } })
-  // @ts-expect-error: fails on null
-  r2[method]({ name: 'UserDetails', params: { id: null } })
-  // @ts-expect-error: and undefined
-  r2[method]({ name: 'UserDetails', params: { id: undefined } })
-  // nested params work too
-  r2[method]({ name: 'nested-c', params: { a: '2', c: '3' } })
-  r2[method]({ name: 'optional' })
-  // optional params are more flexible
-  r2[method]({ name: 'optional', params: {} })
-  r2[method]({ name: 'optional', params: { opt: 'hey' } })
-  r2[method]({ name: 'optional', params: { opt: null } })
-  r2[method]({ name: 'optional', params: { opt: undefined } })
-  // works with symbols too
-  r2[method]({ name: routeName })
-  // @ts-expect-error: but not other symbols
-  r2[method]({ name: Symbol() })
-  // relative push can have any of the params
-  r2[method]({ params: { a: 2 } })
-  r2[method]({ params: {} })
-  r2[method]({ params: { opt: 'hey' } })
-  // @ts-expect-error: but not non existent
-  r2[method]({ params: { fake_param: 'hey' } })
-
-  // routes with no params
-  r2[method]({ name: 'nested' })
-  r2[method]({ name: 'nested', params: {} })
-  // FIXME: is it possible to support this version
-  // @ts-expect-error: does not accept any params
-  r2[method]({ name: 'nested', params: { id: 2 } })
-
-  // paths
-  r2[method]({ path: '/nested' })
-  r2[method]({ path: '/nested/:a/b' })
-  // with an actual param
-  r2[method]({ path: '/nested/a/b' })
-  // NOTE: we actually accept any string because of perf bottlenecks due to tuples
-  r2[method]({ path: '' })
-  r2[method]({ path: '/nope' })
-  r2[method]({ path: '/no-name?query' })
-  r2[method]({ path: '/no-name#hash' })
-
-  r2[method]('/nested')
-  r2[method]('/nested/a/b')
-
-  // NOTE: same as above
-  r2[method]('')
-  r2[method]('/nope')
-  r2[method]('/no-name?query')
-  r2[method]('/no-name#hash')
-}
-
-// NOTE: not possible if we use the named routes as the point is to provide valid routes only
-// @ts-expect-error
-r2.push({} as unknown as RouteLocationRaw)
-// @ts-expect-error
-r2.replace({} as unknown as RouteLocationRaw)
-
-// createMap(r2.options.routes, true).
-
-function joinPath<A extends string, B extends string>(
-  prefix: A,
-  path: B
-): _JoinPath<A, B> {
-  return '' as any
-}
-
-function createMap<R extends Readonly<RouteRecordRaw[]>>(
-  routes: R
-): RouteNamedMap<R> {
-  return {} as any
-}
-
-expectType<'/nested/:a'>(joinPath('/nested', ':a'))
-expectType<'/nested/:a'>(joinPath('/nested/', ':a'))
-expectType<'/:a'>(joinPath('/nested', '/:a'))
-
-expectType<{
-  UserDetails: { params: { id: string }; path: '/users/:id' }
-  nested: { params: {}; path: '/nested' }
-  'nested-a': { params: { a: string }; path: '/nested/:a' }
-  'nested-c': { params: { a: string; c: string }; path: '/nested/:a/b/:c' }
-}>(createMap(r2.options.routes))
-
-expectType<{
-  UserDetails: { params: { nope: string } }
-  // @ts-expect-error
-}>(createMap(r2.options.routes))
-expectType<{
-  UserDetails: { path: '/users' }
-  // @ts-expect-error
-}>(createMap(r2.options.routes))
-expectType<{
-  'nested-c': { path: '/' }
-  // @ts-expect-error
-}>(createMap(r2.options.routes))
-expectType<{
-  'nested-c': { params: { a: string; d: string } }
-  // @ts-expect-error
-}>(createMap(r2.options.routes))
-expectType<{
-  nope: { params: {} }
-  // @ts-expect-error
-}>(createMap(r2.options.routes))
-
-// declare module '../src' {
-declare module '../dist/vue-router' {
-  export interface Config {
-    Router: typeof r2
-  }
-}
-
-const typedRouter = useRouter()
-// this one is true if we comment out the line with Router: typeof r2
-// expectType<Router>(typedRouter)
-expectType<typeof r2>(typedRouter)
-typedRouter.push({ name: 'UserDetails' })
-// @ts-expect-error
-typedRouter.push({ name: 'nope' })
diff --git a/packages/router/test-dts/paths.test-d.ts b/packages/router/test-dts/paths.test-d.ts
deleted file mode 100644 (file)
index 13ebc81..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-import type {
-  ParamsFromPath,
-  _StripRegex,
-  _ExtractParamsOfPath,
-  _RemoveUntilClosingPar,
-  _ExtractModifier,
-  _ModifierExtracTResult,
-} from './'
-import { expectType } from './'
-
-function params<T extends string>(_path: T): ParamsFromPath<T> {
-  return {} as any
-}
-
-// simple
-expectType<Record<any, never>>(params('/static'))
-expectType<{ id: string }>(params('/users/:id'))
-// simulate a part of the string unknown at compilation time
-expectType<{ id: string }>(params(`/${encodeURI('')}/:id`))
-expectType<{ id: readonly [string, ...string[]] }>(params('/users/:id+'))
-expectType<{ id?: string | null | undefined }>(params('/users/:id?'))
-expectType<{ id?: readonly string[] | null | undefined }>(params('/users/:id*'))
-
-expectType<Record<any, never>>(params('/hello/other/thing'))
-// @ts-expect-error
-expectType<{ thing: 'e' }>(params('/hello/other/thing'))
-
-// @ts-expect-error
-expectType<{ other: string }>(params('/users/:id'))
-// @ts-expect-error
-expectType<{ other: string }>(params('/users/static'))
-
-// at beginning
-expectType<{ id: string }>(params('/:id'))
-expectType<{ id: readonly [string, ...string[]] }>(params('/:id+'))
-expectType<{ id?: string | null | undefined }>(params('/:id?'))
-expectType<{ id?: readonly string[] | null | undefined }>(params('/:id*'))
-
-// with trailing path
-expectType<{ id: string }>(params('/users/:id-more'))
-expectType<{ id: readonly [string, ...string[]] }>(params('/users/:id+-more'))
-expectType<{ id?: string | null | undefined }>(params('/users/:id?-more'))
-expectType<{ id?: readonly string[] | null | undefined }>(
-  params('/users/:id*-more')
-)
-
-// multiple
-expectType<{ id: string; b: string }>(params('/users/:id/:b'))
-expectType<{
-  id: readonly [string, ...string[]]
-  b: readonly [string, ...string[]]
-}>(params('/users/:id+/:b+'))
-expectType<{ id?: string | null | undefined; b?: string | null | undefined }>(
-  params('/users/:id?-:b?')
-)
-expectType<{
-  id?: readonly string[] | null | undefined
-  b?: readonly string[] | null | undefined
-}>(params('/users/:id*/:b*'))
-
-// custom regex
-expectType<{ id: string }>(params('/users/:id(one)'))
-expectType<{ id: string }>(params('/users/:id(\\d+)'))
-expectType<{ id: readonly string[] }>(params('/users/:id(one)+'))
-expectType<{ date: string }>(params('/users/:date(\\d{4}-\\d{2}-\\d{2})'))
-expectType<{ a: string }>(params('/:a(pre-(?:\\d{0,5}\\)-end)'))
-
-expectType<{
-  id: readonly [string, ...string[]]
-  b: string
-  c: string
-  d: string
-}>(params('/:id(.*)+/other/:b/:c/:d'))
-
-// special characters
-expectType<{ id: string }>(params('/:id$thing'))
-expectType<{ id: string }>(params('/:id&thing'))
-expectType<{ id: string }>(params('/:id!thing'))
-expectType<{ id: string }>(params('/:id\\*thing'))
-expectType<{ id: string }>(params('/:id\\thing'))
-expectType<{ id: string }>(params("/:id'thing"))
-expectType<{ id: string }>(params('/:id,thing'))
-expectType<{ id: string }>(params('/:id;thing'))
-expectType<{ id: string }>(params('/:id=thing'))
-expectType<{ id: string }>(params('/:id@thing'))
-expectType<{ id: string }>(params('/:id[thing'))
-expectType<{ id: string }>(params('/:id]thing'))
-
-function removeUntilClosingPar<S extends string>(
-  _s: S
-): _RemoveUntilClosingPar<S> {
-  return '' as any
-}
-
-expectType<''>(removeUntilClosingPar(')'))
-expectType<'+'>(removeUntilClosingPar(')+'))
-expectType<'more'>(removeUntilClosingPar(')more'))
-expectType<''>(removeUntilClosingPar('\\w+)'))
-expectType<'/more-url'>(removeUntilClosingPar('\\w+)/more-url'))
-expectType<'/:p'>(removeUntilClosingPar('\\w+)/:p'))
-expectType<'+'>(removeUntilClosingPar('oe)+'))
-expectType<'/:p(o)'>(removeUntilClosingPar('\\w+)/:p(o)'))
-expectType<'/:p(o)'>(removeUntilClosingPar('(?:no\\)?-end)/:p(o)'))
-expectType<'/:p(o(?:no\\)?-end)'>(
-  removeUntilClosingPar('-end)/:p(o(?:no\\)?-end)')
-)
-expectType<':new(eg)other'>(removeUntilClosingPar('customr):new(eg)other'))
-expectType<':new(eg)+other'>(removeUntilClosingPar('customr):new(eg)+other'))
-expectType<'/:new(eg)+other'>(removeUntilClosingPar('customr)/:new(eg)+other'))
-expectType<'?/:new(eg)+other'>(
-  removeUntilClosingPar('customr)?/:new(eg)+other')
-)
-
-function stripRegex<S extends string>(_s: S): _StripRegex<S> {
-  return '' as any
-}
-
-expectType<'+/edit/'>(stripRegex('(\\d+)+/edit/'))
-expectType<'*'>(stripRegex('(.*)*'))
-expectType<'?/rest'>(stripRegex('?/rest'))
-expectType<'*'>(stripRegex('*'))
-expectType<'-other-stuff'>(stripRegex('-other-stuff'))
-expectType<'/edit'>(stripRegex('/edit'))
-expectType<'?/rest/:other(.*)*'>(stripRegex('?/rest/:other(.*)*'))
-expectType<'+/edit/:other(.*)*'>(stripRegex('(\\d+)+/edit/:other(.*)*'))
-expectType<'?/rest/:other(.*)/more/:b(.*)'>(
-  stripRegex('?/rest/:other(.*)/more/:b(.*)')
-)
-
-function extractModifier<S extends string>(_s: S): _ExtractModifier<S> {
-  return {} as any
-}
-
-expectType<_ModifierExtracTResult<'', ''>>(extractModifier(''))
-expectType<_ModifierExtracTResult<'', '-rest'>>(extractModifier('-rest'))
-expectType<_ModifierExtracTResult<'', 'edit'>>(extractModifier('edit'))
-expectType<_ModifierExtracTResult<'+', ''>>(extractModifier('+'))
-expectType<_ModifierExtracTResult<'+', '/edit'>>(extractModifier('+/edit'))
-expectType<_ModifierExtracTResult<'+', '/edit/:a?'>>(
-  extractModifier('+/edit/:a?')
-)