import { describe, expectTypeOf, it } from 'vitest'
import { MatcherPatternPathDynamic } from './matcher-pattern'
import { PARAM_INTEGER_SINGLE } from './param-parsers/numbers'
-import { PATH_PARAM_DEFAULT_PARSER } from './param-parsers'
+import { PATH_PARAM_PARSER_DEFAULTS } from './param-parsers'
import { PATH_PARAM_SINGLE_DEFAULT } from './param-parsers'
describe('MatcherPatternPathCustomParams', () => {
it('can be generic', () => {
const matcher = new MatcherPatternPathDynamic(
/^\/users\/([^/]+)$/i,
- { userId: { ...PATH_PARAM_DEFAULT_PARSER } },
+ { userId: { ...PATH_PARAM_PARSER_DEFAULTS } },
['users', 0]
)
return parser
}
-export const PATH_PARAM_DEFAULT_GET = (
- value: string | string[] | null | undefined
-) => value ?? null
-export const PATH_PARAM_DEFAULT_SET = (
- value: string | string[] | null | undefined
-) => (value && Array.isArray(value) ? value.map(String) : String(value)) // TODO: `(value an null | undefined)` for types
-
export const PATH_PARAM_SINGLE_DEFAULT: ParamParser<string, string> = {}
-export const PATH_PARAM_DEFAULT_PARSER = {
- get: PATH_PARAM_DEFAULT_GET,
- set: PATH_PARAM_DEFAULT_SET,
+
+/**
+ * Default parser for params that will keep values as is, and will use `String()`
+ */
+export const PARAM_PARSER_DEFAULTS = {
+ get: value => value ?? null,
+ set: value =>
+ value == null
+ ? null
+ : Array.isArray(value)
+ ? value.map(v => (v == null ? null : String(v)))
+ : String(value),
} satisfies ParamParser
+export const PATH_PARAM_PARSER_DEFAULTS = {
+ get: value => value ?? null,
+ set: value =>
+ value == null
+ ? null
+ : Array.isArray(value)
+ ? value.map(String)
+ : String(value),
+} satisfies ParamParser<string | string[] | null, string | string[] | null>
+
export type { ParamParser }
export { PARAM_PARSER_INT } from './numbers'
import { ParamParser } from './types'
export const PARAM_INTEGER_SINGLE = {
- get: (value: string) => {
+ get: (value: string | null) => {
const num = Number(value)
if (value && Number.isInteger(num)) {
return num
throw miss()
},
set: (value: number) => String(value),
-} satisfies ParamParser<number, string>
+} satisfies ParamParser<number, string | null>
export const PARAM_INTEGER_OPTIONAL = {
get: (value: string | null) =>
} satisfies ParamParser<number | null, string | null>
export const PARAM_INTEGER_REPEATABLE = {
- get: (value: string[]) => value.map(PARAM_INTEGER_SINGLE.get),
+ get: (value: (string | null)[]) => value.map(PARAM_INTEGER_SINGLE.get),
set: (value: number[]) => value.map(PARAM_INTEGER_SINGLE.set),
-} satisfies ParamParser<number[], string[]>
+} satisfies ParamParser<number[], (string | null)[]>
export const PARAM_INTEGER_REPEATABLE_OPTIONAL = {
get: (value: string[] | null) =>
+import { MatcherQueryParamsValue } from '../matcher-pattern'
+
/**
* Defines a parser that can read a param from the url (string-based) and
* transform it into a more complex type, or vice versa.
* @see MatcherPattern
*/
export interface ParamParser<
- TOut = string | string[] | null,
- TIn extends string | string[] | null = string | string[] | null,
+ TOut = MatcherQueryParamsValue,
+ TIn extends MatcherQueryParamsValue = MatcherQueryParamsValue,
> {
get?: (value: NoInfer<TIn>) => TOut
set?: (value: NoInfer<TOut>) => TIn
}
+// TODO: I wonder if native param parsers should follow this or similar
+// these parsers can be used for both query and path params
+// export type ParamParserBoth<T> = ParamParser<T | T[] | null>
+
/**
* Generic type for a param parser that can handle both single and repeatable params.
*