From: Eduardo San Martin Morote Date: Tue, 3 May 2022 14:37:40 +0000 (+0200) Subject: feat(types): add RouteNamepMap type X-Git-Tag: v4.1.0~109 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=afd8f83be8f5c365b568ce80d8b185896c7eb07b;p=thirdparty%2Fvuejs%2Frouter.git feat(types): add RouteNamepMap type --- diff --git a/src/index.ts b/src/index.ts index 46ead1ad..d1eb2d13 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,6 +68,7 @@ export type { _RemoveRegexpFromParam, _RemoveUntilClosingPar, } from './types/paths' +export type { RouteNamedMap } from './types/named' export { createRouter } from './router' export type { Router, RouterOptions, RouterScrollBehavior } from './router' diff --git a/src/types/index.ts b/src/types/index.ts index 7ececb4c..5124a45f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -215,10 +215,6 @@ export interface _RouteRecordBase extends PathParserOptions { */ redirect?: RouteRecordRedirectOption - /** - * Array of nested routes. - */ - children?: RouteRecordRaw[] /** * Aliases for the record. Allows defining extra paths that will behave like a * copy of the record. Allows having paths shorthands like `/users/:id` and @@ -298,7 +294,7 @@ export interface RouteRecordSingleViewWithChildren extends _RouteRecordBase { /** * Array of nested routes. */ - children: RouteRecordRaw[] + children: Readonly /** * Allow passing down params as props to the component rendered by `router-view`. diff --git a/src/types/named.ts b/src/types/named.ts index d238da59..ce3cf33c 100644 --- a/src/types/named.ts +++ b/src/types/named.ts @@ -1,4 +1,6 @@ -import { Router } from '../router' +import type { RouteRecordRaw } from '.' +import type { Router } from '../router' +import { JoinPath, ParamsFromPath } from './paths' /** * This will flat the routes into an object with `key` === `router.name` @@ -43,3 +45,36 @@ export type ExtractRoutes = ExtractNamedRoutes< * ``` */ export interface NamedLocationMap {} + +export type RouteNamedMap< + Routes extends Readonly, + Prefix extends string = '' +> = Routes extends readonly [infer R, ...infer Rest] + ? Rest extends Readonly + ? (R extends { + name?: infer Name + path: infer Path + children?: infer Children + } + ? Path extends string + ? (Name extends string + ? { + [N in Name]: ParamsFromPath> + } + : { + // NO_NAME: 1 + }) & + (Children extends Readonly + ? RouteNamedMap> + : { + // NO_CHILDREN: 1 + }) + : never // Path must be a string + : { + // EMPTY: 1 + }) & + RouteNamedMap + : never // R must be a valid route record + : { + // END: 1 + } diff --git a/src/types/paths.ts b/src/types/paths.ts index 72d965fb..4d32cfd4 100644 --- a/src/types/paths.ts +++ b/src/types/paths.ts @@ -1,3 +1,15 @@ +/** + * 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

= string extends P + ? PathParams // Generic version + : _ExtractParamsPath<_RemoveRegexpFromParam

> + /** * Generic possible params from a path (after parsing). */ @@ -56,13 +68,6 @@ export type _ExtractParamsPath

= _ExtractParamsPath : {} -/** - * Extract an object of params given a path like `/users/:id`. - */ -export type ParamsFromPath

= string extends P - ? PathParams // Generic version - : _ExtractParamsPath<_RemoveRegexpFromParam

> - /** * Gets the possible type of a param based on its modifier M. * @@ -275,3 +280,12 @@ export type _ExtractPathParamKeys

= export type ParamKeysFromPath

= string extends P ? readonly PathParserParamKey[] // Generic version : _ExtractPathParamKeys<_RemoveRegexpFromParam

> + +export type JoinPath< + Prefix extends string, + Path extends string +> = Path extends `/${string}` + ? Path + : '' extends Prefix + ? never + : `${Prefix}${Prefix extends `${string}/` ? '' : '/'}${Path}` diff --git a/test-dts/namedRoutes.test-d.ts b/test-dts/namedRoutes.test-d.ts index 5fea37fa..ce187e7e 100644 --- a/test-dts/namedRoutes.test-d.ts +++ b/test-dts/namedRoutes.test-d.ts @@ -3,10 +3,17 @@ import { Router, ExtractRoutes, createRouter, + createWebHistory, + RouteRecordRaw, + expectType, + RouteNamedMap, } from './index' import { DefineComponent } from 'vue' +import { JoinPath } from 'src/types/paths' declare const Comp: DefineComponent +declare const component: DefineComponent +declare const components: { default: DefineComponent } const routes = [ { @@ -46,6 +53,74 @@ const routes = [ }, ] as const +export function defineRoutes< + Path extends string, + Routes extends Readonly[]> +>(routes: Routes): Routes { + return routes +} + +const r2 = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/users/:id', name: 'UserDetails', component }, + { path: '/no-name', /* no name */ component }, + { + path: '/nested', + name: 'nested', + children: [ + { + path: ':a', + name: 'nested-a', + children: [ + { + path: 'b', + children: [{ path: ':c', name: 'nested-c', component }], + }, + ], + }, + ], + }, + ] as const, +}) + +function joinPath( + prefix: A, + path: B +): JoinPath { + return '' as any +} + +function createMap>( + routes: R +): RouteNamedMap { + return {} as any +} + +expectType<'/nested/:a'>(joinPath('/nested', ':a')) +expectType<'/nested/:a'>(joinPath('/nested/', ':a')) +expectType<'/:a'>(joinPath('/nested', '/:a')) + +expectType<{ + UserDetails: { id: string } + nested: {} + 'nested-a': { a: string } + 'nested-c': { a: string; c: string } +}>(createMap(r2.options.routes)) + +expectType<{ + UserDetails: { nope: string } + // @ts-expect-error +}>(createMap(r2.options.routes)) +expectType<{ + 'nested-c': { a: string; d: string } + // @ts-expect-error +}>(createMap(r2.options.routes)) +expectType<{ + nope: {} + // @ts-expect-error +}>(createMap(r2.options.routes)) + declare const typed: ExtractNamedRoutes typed['my-other-path']