Skip to content

Instantly share code, notes, and snippets.

@lupuszr
Created April 12, 2022 19:01
Show Gist options
  • Save lupuszr/5d30efb748217f9f7da802bbd311179f to your computer and use it in GitHub Desktop.
Save lupuszr/5d30efb748217f9f7da802bbd311179f to your computer and use it in GitHub Desktop.
typelevel url parser in typescript
type ParamParser<T extends string> = T extends `${infer param}/${infer rest}` ? [param, ...ParamParser<rest>] : [T]
type TakeApart<T extends string> = T extends `${infer p}=${infer v}` ? [p, v] : []
type QueryParser<T extends string> = T extends `${infer el}&${infer rest}` ? [TakeApart<el>, ...QueryParser<rest>]: [TakeApart<T>]
type urlWithParamAndQuery<T extends string, FB = never> = T extends `${infer Base}/${infer Param}?${infer Query}` ? {base: Base, param: ParamParser<Param>, query: QueryParser<Query>} : FB;
type urlWithQuery<T extends string, FB = never> = T extends `${infer Base}?${infer Query}` ? {base: Base, param: [], query: QueryParser<Query>} : FB;
type urlWithParam<T extends string, FB = never> = T extends `${infer Base}/${infer Param}` ? {base: Base, param: ParamParser<Param>, query: []} : FB;
type staticUrl<T extends string> = {base: T, param: [], query: []};
type url<T extends string> = urlWithParamAndQuery<T, urlWithParam<T, urlWithQuery<T, staticUrl<T>>>>;
type Norm<T extends Array<unknown>> = T extends [infer Z, ...infer rest] ? [1, ...Norm<rest>] : [];
type And<T extends boolean, K extends boolean> = T extends false ? false : (K extends false ? false : true)
type MatchQuery<T, K> = T extends [infer K1, infer V1] ?
K extends [infer K2, infer V2] ?
K1 extends K2 ?
V1 extends V2 ?
true: false : false: false: false;
type Test1 = MatchQuery<["b", "b"], [string, "b"]>
// WIP
// type CompareQuery<T, K > = T extends [infer H1, ...infer tail1] ?
// K extends [infer H2, ...infer tail2] ?
// tail1 extends Array<Array<string>> ?
// tail2 extends Array<Array<string>> ?
// And<MatchQuery<H1, H2>, CompareQuery<tail1, tail2>> : false : false : false : (T extends [infer H1] ? K extends [infer H2] ? MatchQuery<H1, H2> : H1 : "pina");
//And<MatchQuery<H1, H2>, CompareQuery<tail1, tail2>>
// type Test2 = CompareQuery<[["a", "b"], ["c", "d"]], [["a", "b"], ["c", "d"]]>
type Match<T extends string, R extends string> = [url<T>["base"] extends url<R>["base"] ? true : false, Norm<url<T>["param"]> extends Norm<url<R>["param"]> ? true : false, Norm<url<T>["query"]> extends Norm<url<R>["query"]> ? true : false];
type CompareRoute<T extends string, R extends string> = Match<T, R> extends [true, true] ? url<R> : never;
type Find<Comparator extends string, T extends Array<string>> = T extends [infer Z, ...infer rest] ?
Z extends string ?
Match<Comparator, Z> extends [true, true, true] ?
Z : rest extends Array<string> ? Find<Comparator, rest> : never : never : never;
type A = Find<"user/12/aleksa", ["user/12?pina=geci", "user/:userId/:name", "2"]>
type ki = CompareRoute<"user/12?name=aleksa", "user/:userId?name=string">
type RoutesGET = [
["user/me", Promise<1337>],
["user/:userId?name=string", "this is sparta!!"],
["miner/2433", 2000]
]
type First<T extends Array<unknown>> = T extends [infer H, ...infer tail] ?
H extends [infer Fst, infer Snd] ?
[Fst, ...First<tail>] : [] : [];
type Second<T extends Array<unknown>> = T extends [infer H, ...infer tail] ?
H extends [infer Fst, infer Snd] ?
[Snd, ...Second<tail>] : [] : [];
type FindRouteValue<A extends string, T extends Array<unknown>> = T extends [infer H, ...infer tail] ?
H extends [infer Fst, infer Snd] ?
Fst extends A ? Snd : FindRouteValue<A, tail> : [] : [];
type R = First<RoutesGET>;
type Test3 = FindRouteValue<R[2], RoutesGET>
function get<T extends string, K = url<Find<T, First<RoutesGET>>>>(r: T): FindRouteValue<Find<T, First<RoutesGET>>, RoutesGET> {
throw "Not implemented"
}
get("user/aleksa?name=Hello")
get("user/me")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment