Advanced TypeScript
TypeScript Route Validation and Autocomplete
Source: https://dev.to/hichemtab-tech/the-day-i-truly-realized-i-was-a-junior-developer-kgp
type MergePath<T extends string | undefined, P extends string> =
T extends ""
? P
: P extends "/" | ""
? `${P}${T}`
: `${P}/${T}`;
type ExtractRouteType<T extends RouteObject, P extends string> =
T extends { path: string }
? { route: P, paramsKeys: ExtractPathParams<P> }
: never;
type ExtractRoute<T extends RouteObject, P extends string> =
(T extends { id: string, path: string }
? { [K in T["id"]]: ExtractRouteType<T, P> }
: {}) & ExtractRoutes<T["children"], P>;
type ExtractRoutes<T extends RouteObject[] | undefined, P extends string = ""> =
T extends [infer R extends RouteObject, ...infer S extends RouteObject[]]
? ExtractRoute<R, MergePath<R["path"], P>> & ExtractRoutes<S, P>
: {};
type ExtractPathParams<Path extends string> =
Path extends `${string}:${infer Param}/${infer Rest}`
? [Param, ...ExtractPathParams<`/${Rest}`>]
: Path extends `${string}:${infer Param}`
? [Param]
: [];
export type RouteById =
ExtractRoutes<typeof routeObjects> extends infer U
? { [K in keyof U]: U[K] }
: never;
Description
This TypeScript code defines a set of utility types to extract and manipulate route information from a given set of route objects. Here's a breakdown of what each part does:
MergePath
type MergePath<T extends string | undefined, P extends string> =
T extends ""
? P
: P extends "/" | ""
? `${P}${T}`
: `${P}/${T}`;
- Purpose: Concatenates two path segments
TandPinto a single path. - Logic:
- If
Tis an empty string, returnP. - If
Pis "/" or an empty string, concatenatePandT. - Otherwise, concatenate
PandTwith a "/" in between.
- If
ExtractRouteType
type ExtractRouteType<T extends RouteObject, P extends string> =
T extends { path: string }
? { route: P, paramsKeys: ExtractPathParams<P> }
: never;
- Purpose: Extracts route information from a route object
Tand a pathP. - Logic:
- If
Thas apathproperty, return an object withrouteset toPandparamsKeysset to the extracted path parameters fromP. - Otherwise, return
never.
- If
ExtractRoute
type ExtractRoute<T extends RouteObject, P extends string> =
(T extends { id: string, path: string }
? { [K in T["id"]]: ExtractRouteType<T, P> }
: {}) & ExtractRoutes<T["children"], P>;
- Purpose: Extracts route information from a route object
Tand its children. - Logic:
- If
Thasidandpathproperties, create an object with the key asT["id"]and value as the result ofExtractRouteType<T, P>. - Combine this with the result of
ExtractRoutesapplied toT["children"]andP.
- If
ExtractRoutes
type ExtractRoutes<T extends RouteObject[] | undefined, P extends string = ""> =
T extends [infer R extends RouteObject, ...infer S extends RouteObject[]]
? ExtractRoute<R, MergePath<R["path"], P>> & ExtractRoutes<S, P>
: {};
- Purpose: Recursively extracts route information from an array of route objects
T. - Logic:
- If
Tis a non-empty array, extract route information from the first elementRand merge it with the result ofExtractRoutesapplied to the rest of the arrayS. - If
Tis empty or undefined, return an empty object.
- If
ExtractPathParams
type ExtractPathParams<Path extends string> =
Path extends `${string}:${infer Param}/${infer Rest}`
? [Param, ...ExtractPathParams<`/${Rest}`>]
: Path extends `${string}:${infer Param}`
? [Param]
: [];
Purpose
The ExtractPathParams type is a utility type that extracts the parameter names from a given URL path string. It returns an array of parameter names found in the path.
How It Works
Pattern Matching with Template Literal Types:
- The type uses TypeScript's template literal types to match and extract parts of the string.
Conditional Types:
- The type uses conditional types to recursively extract parameters.
Logic:
-
First Condition:
Path extends ${string}:${infer Param}/${infer Rest}- This checks if the
Pathcontains a parameter (denoted by:Param) followed by a slash (/). - If it matches, it extracts
Paramand the rest of the path (Rest). - It returns an array with
Paramand recursively callsExtractPathParamson the rest of the path.
- This checks if the
-
Second Condition:
Path extends ${string}:${infer Param}- This checks if the
Pathcontains a parameter (denoted by:Param) without a following slash. - If it matches, it extracts
Param. - It returns an array with
Param.
- This checks if the
-
Default Case:
[]- If neither condition matches, it returns an empty array, indicating no parameters are present.
Example
For the path "/user/:id/profile/:section", ExtractPathParams would work as follows:
- First, it matches
"/user/:id/profile/:section"with the first condition, extractingidand the rest of the path"/profile/:section". - Then, it recursively processes
"/profile/:section", extractingsection. - The final result is
["id", "section"].
This type is useful for extracting dynamic parameter names from URL paths in a type-safe manner, which can be particularly helpful in routing systems.
RouteById
export type RouteById =
ExtractRoutes<typeof routeObjects> extends infer U
? { [K in keyof U]: U[K] }
: never;
- Purpose: Creates a type that maps route IDs to their extracted route information.
- Logic:
- Apply
ExtractRoutestorouteObjectsand infer the result asU. - Create a type with keys from
Uand values as the corresponding values fromU.
- Apply
Summary
This code is designed to take a nested structure of route objects and produce a type that maps route IDs to their corresponding route information, including paths and parameter keys. This can be useful for type-safe route management in a TypeScript application.