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
T
andP
into a single path. - Logic:
- If
T
is an empty string, returnP
. - If
P
is "/" or an empty string, concatenateP
andT
. - Otherwise, concatenate
P
andT
with 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
T
and a pathP
. - Logic:
- If
T
has apath
property, return an object withroute
set toP
andparamsKeys
set 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
T
and its children. - Logic:
- If
T
hasid
andpath
properties, create an object with the key asT["id"]
and value as the result ofExtractRouteType<T, P>
. - Combine this with the result of
ExtractRoutes
applied 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
T
is a non-empty array, extract route information from the first elementR
and merge it with the result ofExtractRoutes
applied to the rest of the arrayS
. - If
T
is 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
Path
contains a parameter (denoted by:Param
) followed by a slash (/
). - If it matches, it extracts
Param
and the rest of the path (Rest
). - It returns an array with
Param
and recursively callsExtractPathParams
on the rest of the path.
- This checks if the
-
Second Condition:
Path extends ${string}:${infer Param}
- This checks if the
Path
contains 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, extractingid
and 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
ExtractRoutes
torouteObjects
and infer the result asU
. - Create a type with keys from
U
and 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.