From e2966b0bbf338239b1c6b54cb2ad2257bdbb37c6 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 11 Aug 2025 10:05:48 +0200 Subject: [PATCH] refactor: put TOut type param first --- .../src/router/index.ts | 8 +-- packages/router/src/experimental/index.ts | 3 +- .../matchers/matcher-pattern.ts | 54 ++++++++++++------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/packages/experiments-playground/src/router/index.ts b/packages/experiments-playground/src/router/index.ts index 27789265..c8b5b2b1 100644 --- a/packages/experiments-playground/src/router/index.ts +++ b/packages/experiments-playground/src/router/index.ts @@ -3,10 +3,9 @@ import { experimental_createRouter, createStaticResolver, MatcherPatternPathStatic, - MatcherPatternPathStar, MatcherPatternPathCustomParams, normalizeRouteRecord, - PARAM_INTEGER, + PARAM_PARSER_INTEGER, } from 'vue-router/experimental' import type { EXPERIMENTAL_RouteRecordNormalized_Matchable, @@ -144,7 +143,10 @@ const r_profiles_detail = normalizeRouteRecord({ path: new MatcherPatternPathCustomParams( /^\/profiles\/([^/]+)$/i, { - userId: PARAM_INTEGER, + // this version handles all kind of params but in practice, + // the generation should recognize this is a single required param + // and therefore userId is of type number + userId: PARAM_PARSER_INTEGER, }, ['profiles', 0] ), diff --git a/packages/router/src/experimental/index.ts b/packages/router/src/experimental/index.ts index ab6f1c6c..595128ee 100644 --- a/packages/router/src/experimental/index.ts +++ b/packages/router/src/experimental/index.ts @@ -25,7 +25,8 @@ export { MatcherPatternPathCustomParams, // custom param parsers // TODO: find a more elegant format than having 4 variants per param type - PARAM_INTEGER, + PARAM_PARSER_INTEGER, + PARAM_INTEGER_SINGLE, PARAM_NUMBER_OPTIONAL, PARAM_NUMBER_REPEATABLE, PARAM_NUMBER_REPEATABLE_OPTIONAL, diff --git a/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts b/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts index 72d6ca0a..89fc978c 100644 --- a/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts +++ b/packages/router/src/experimental/route-resolver/matchers/matcher-pattern.ts @@ -121,20 +121,16 @@ export class MatcherPatternPathStar // new MatcherPatternPathStatic('/team') export interface Param_GetSet< - TIn extends string | string[] | null | undefined = - | string - | string[] - | null - | undefined, TOut = string | string[] | null, + TIn extends string | string[] | null = string | string[] | null, > { get?: (value: NoInfer) => TOut set?: (value: NoInfer) => TIn } export type ParamParser_Generic = - | Param_GetSet - | Param_GetSet + | Param_GetSet + | Param_GetSet // TODO: these are possible values for optional params // | null | undefined @@ -147,7 +143,7 @@ export type ParamParser_Generic = export function defineParamParser(parser: { get?: (value: TIn) => TOut set?: (value: TOut) => TIn -}): Param_GetSet { +}): Param_GetSet { return parser } @@ -189,7 +185,7 @@ export const PATH_PARAM_DEFAULT_PARSER: Param_GetSet = { */ export type ParamsFromParsers

> = { - [K in keyof P]: P[K] extends Param_GetSet + [K in keyof P]: P[K] extends Param_GetSet ? unknown extends TOut // if any or unknown, use the value of TIn, which defaults to string | string[] ? TIn : TOut @@ -199,7 +195,7 @@ export type ParamsFromParsers

> = { interface MatcherPatternPathCustomParamOptions< TIn extends string | string[] | null = string | string[] | null, TOut = string | string[] | null, -> extends Param_GetSet { +> extends Param_GetSet { repeat?: boolean // NOTE: not needed because in the regexp, the value is undefined if // the group is optional and not given @@ -221,7 +217,7 @@ type ExtractParamTypeFromOptions = { const IS_INTEGER_RE = /^-?[1-9]\d*$/ -export const PARAM_INTEGER = { +export const PARAM_INTEGER_SINGLE = { get: (value: string) => { if (IS_INTEGER_RE.test(value)) { const num = Number(value) @@ -232,26 +228,46 @@ export const PARAM_INTEGER = { throw miss() }, set: (value: number) => String(value), -} satisfies Param_GetSet +} satisfies Param_GetSet export const PARAM_NUMBER_OPTIONAL = { get: (value: string | null) => - value == null ? null : PARAM_INTEGER.get(value), + value == null ? null : PARAM_INTEGER_SINGLE.get(value), set: (value: number | null) => - value != null ? PARAM_INTEGER.set(value) : null, -} satisfies Param_GetSet + value != null ? PARAM_INTEGER_SINGLE.set(value) : null, +} satisfies Param_GetSet export const PARAM_NUMBER_REPEATABLE = { - get: (value: string[]) => value.map(PARAM_INTEGER.get), - set: (value: number[]) => value.map(PARAM_INTEGER.set), -} satisfies Param_GetSet + get: (value: string[]) => value.map(PARAM_INTEGER_SINGLE.get), + set: (value: number[]) => value.map(PARAM_INTEGER_SINGLE.set), +} satisfies Param_GetSet export const PARAM_NUMBER_REPEATABLE_OPTIONAL = { get: (value: string[] | null) => value == null ? null : PARAM_NUMBER_REPEATABLE.get(value), set: (value: number[] | null) => value != null ? PARAM_NUMBER_REPEATABLE.set(value) : null, -} satisfies Param_GetSet +} satisfies Param_GetSet + +/** + * Native Param parser for integers. + * + * @internal + */ +export const PARAM_PARSER_INTEGER: Param_GetSet = { + get: value => + Array.isArray(value) + ? PARAM_NUMBER_REPEATABLE.get(value) + : value != null + ? PARAM_INTEGER_SINGLE.get(value) + : null, + set: value => + Array.isArray(value) + ? PARAM_NUMBER_REPEATABLE.set(value) + : value != null + ? PARAM_INTEGER_SINGLE.set(value) + : null, +} export class MatcherPatternPathCustomParams< TParamsOptions, -- 2.47.3