ts-routes
Version:
Strongly typed routes management
48 lines (47 loc) • 3.67 kB
TypeScript
import { IParseOptions, IStringifyOptions } from "qs";
import { Optionality } from "./helpers";
import PathParamDescription from "./PathParamDescription";
import QueryParamDescription from "./QueryParamDescription";
import RouteDescription from "./RouteDescription";
type MapToRoute<TRouteDescription extends RouteDescription<any, any, any>> = {
(...params: [...args: PathPatternArgs<TRouteDescription>, stringifyOptions?: IStringifyOptions]): string;
parseQuery: (query: string, parseOptions?: IParseOptions) => GetParsedQuery<TRouteDescription>;
pattern: string;
} & {
[TKey in keyof RouteChildren<TRouteDescription>]: MapToRoute<AddParams<RouteChildren<TRouteDescription>[TKey], TRouteDescription>>;
};
type RouteChildren<TRouteDescription extends RouteDescription<any, any, any>> = TRouteDescription extends RouteDescription<any, any, infer TChildren> ? TChildren : never;
type AddParams<TDestRoute extends RouteDescription<any, any, any>, TSourceRoute extends RouteDescription<any, any, any>> = TDestRoute extends RouteDescription<infer TDestPathParamsDescription, infer TDestQueryParamsDesrciption, infer TChildren> ? TSourceRoute extends RouteDescription<infer TSourcePathParamsDescription, infer TSourceQueryParamsDescription, any> ? RouteDescription<[
...TSourcePathParamsDescription,
...TDestPathParamsDescription
], TDestQueryParamsDesrciption & TSourceQueryParamsDescription, TChildren> : never : never;
type MakeOptional<T> = {} extends T ? [T?] : [T];
type PathPatternArgs<TRouteDescription extends RouteDescription<any, any, any>> = [
...pathParams: MakeOptional<MapPathParams<GetPathParams<TRouteDescription>>>,
...queryParams: MakeOptional<MapQueryParams<GetQueryParams<TRouteDescription>>>
];
type GetPathParams<TRouteDescription extends RouteDescription<any, any, any>> = TRouteDescription extends RouteDescription<infer TPathParams, any, any> ? TPathParams : never;
type GetQueryParams<TRouteDescription extends RouteDescription<any, any, any>> = TRouteDescription extends RouteDescription<any, infer TQueryParams, any> ? TQueryParams : never;
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
type MapPathParams<TPathParams extends PathParamDescription<string, Optionality>[]> = UnionToIntersection<{
[T in keyof TPathParams]: GetParam<TPathParams[T]>;
}[number]>;
type SingleOrArray<T> = T | T[];
type MapQueryParams<TQueryParams extends Record<string, QueryParamDescription<any, Optionality>>> = {
[TName in keyof TQueryParams as TQueryParams[TName] extends QueryParamDescription<any, "optional"> ? TName : never]?: SingleOrArray<GetQueryResultType<TQueryParams[TName]>>;
} & {
[TName in keyof TQueryParams as TQueryParams[TName] extends QueryParamDescription<any, "required"> ? TName : never]: SingleOrArray<GetQueryResultType<TQueryParams[TName]>>;
};
type GetQueryResultType<TQueryParam extends QueryParamDescription<any, Optionality>> = TQueryParam extends QueryParamDescription<infer TReturnType, Optionality> ? TReturnType : never;
type GetParam<TPathParam> = TPathParam extends PathParamDescription<infer TName, infer TOptional> ? TOptional extends "optional" ? {
[T in TName]?: string;
} : {
[T in TName]: string;
} : never;
type GetParsedQuery<TRouteDescription extends RouteDescription<any, any, any>> = MapQueryParams<GetQueryParams<TRouteDescription>>;
export default function createRouting<TRoutes extends {
readonly [name: string]: RouteDescription<any, any, any>;
}>(routes: TRoutes, options?: IParseOptions & IStringifyOptions): {
readonly [TKey in keyof TRoutes]: MapToRoute<TRoutes[TKey]>;
};
export {};