Skip to content

Instantly share code, notes, and snippets.

@dac09
Last active July 8, 2021 14:22
Show Gist options
  • Save dac09/73fe5c04571f55095a9ef446bb6263e9 to your computer and use it in GitHub Desktop.
Save dac09/73fe5c04571f55095a9ef446bb6263e9 to your computer and use it in GitHub Desktop.
ParamParsing
type ParamType<constraint> = constraint extends 'Int' ? number : constraint extends 'Boolean' ? boolean : constraint extends 'Float' ? number : string
type RouteParams<Route> = Route extends `${string}/${infer Rest}`
? A.Compute<ParsedParams<Rest>>
: {}
type QueryParams = Record<string | number, string | number | boolean>
type ParsedParams<PartialRoute> =
// {a:Int}/[...moar]
PartialRoute extends `{${infer Param}:${infer Constraint}}/${infer Rest}`
? // check for greedy match e.g. {b}/{c:Int}
// Param = b}/{c, Rest2 = {c, Constrait = Int so we reconstruct the old one {c + : + Int + }
Param extends `${infer Param2}}/${infer Rest2}`
? { [ParamName in Param2]: string } &
ParsedParams<`${Rest2}:${Constraint}}`> &
ParsedParams<`${Rest}`>
: { [Entry in Param]: ParamType<Constraint> } & ParsedParams<`${Rest}`>
: // has type, but at the end e.g.{d:Int}
PartialRoute extends `{${infer Param}:${infer Constraint}}`
? // Greedy match order 2
Param extends `${infer Param2}}/${infer Rest2}`
? { [ParamName in Param2]: string } &
ParsedParams<`${Rest2}:${Constraint}}`>
: { [Entry in Param]: ParamType<Constraint> }
: // no type, but has stuff ater it {c}/{d}
PartialRoute extends `{${infer Param}}/${infer Rest}`
? { [ParamName in Param]: string } & ParsedParams<`${Rest}`>
: // last one with no type e.g. {d}
PartialRoute extends `{${infer Param}}`
? { [ParamName in Param]: string }
: // if theres a non param
PartialRoute extends `${string}/${infer Rest}`
? ParsedParams<`${Rest}`>
: {}
/// ====== TEST CASES =========
type mixed = RouteParams<'/path/{b:Int}/{c}/{d}'>
type oneEach = RouteParams<'/path/{b:Int}/{c}/{d:Boolean}'>
type oneEachDiffOrder = RouteParams<'/path/{b}/{c:Int}/{d:Boolean}'>
type noTypesatall3 = RouteParams<'/path/{b}/{c}/{d}'>
type aftersOnly2 = RouteParams<'/path/{b}/{c}'>
type aftersOnly4 = RouteParams<'/path/{b}/{c}/{d}/{e}'>
type aftWithTypes = RouteParams<'/path/{b:Int}/{c}'>
type aftWithTypes2 = RouteParams<'/path/{b:Int}/{c:Boolean}'>
// greedy matching {b}/{c:Int}
type aftWithTypes3 = RouteParams<'/path/{b}/{c:Int}/{d:Boolean}'>
// greedy matching reversed {b:Int}/{c}
type aftWithTypes4 = RouteParams<'/path/{b:Int}/{c}/{d:Boolean}'>
type aftMIxed = RouteParams<'/path/{b:Int}/{c:Boolean}/{d}/{e:Int}'>
type aftMIxed2 = RouteParams<'/path/{b:Int}/{c:Boolean}/{d}/{e:String}/{f}'>
type nonParamsInTheMiddle =
RouteParams<'/user/{userId}/edit/likes/{likeId:Int}'>
type badParams =
RouteParams<'/mega/{bId:Int}/s/d/{year:Int}/{month:Int}/{monthDates}/e/{eId:Int}/s'>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment