Last active
April 19, 2024 19:58
-
-
Save sylvaindethier/daf830e610afce09360fff5506a328b1 to your computer and use it in GitHub Desktop.
SolidJS type safe for `useParam` from a `RouteDefinition`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { RouteDefinition } from "@solidjs/router"; | |
/** | |
* Refine a Type | |
* @example | |
* ```ts | |
* type Refined = Refine<{ foo: string, bar: unknown[] }, { foo: `${number}` }> | |
* // ^? { foo: `${number}`, bar: unknown[] } | |
* ``` | |
*/ | |
export type Refine<T, P extends Partial<T>> = { | |
[k in keyof T]: T[k] & P[k]; | |
}; | |
/** | |
* Prettify helper | |
* @see https://www.totaltypescript.com/concepts/the-prettify-helper | |
*/ | |
export type Prettify<T> = { | |
[k in keyof T]: T[k]; | |
} & unknown; | |
// SHOULD be: import type { MatchFilter } from "@solidjs/router"; | |
// but `@solidjs/router` does NOT expose this type | |
type MatchFilter = NonNullable< | |
NonNullable<RouteDefinition["matchFilters"]>[string] | |
>; | |
// The `matchFilters` of a RouteDefinition describes its Params | |
type RD$matchFilters<RD extends RouteDefinition> = NonNullable< | |
RD["matchFilters"] | |
>; | |
// Convert MatchFilter to string value | |
type MF$toString<MF extends MatchFilter | undefined> = | |
MF extends readonly string[] | |
? MF[number] | |
: MF extends RegExp | |
? `${string}` | |
: MF extends (s: string) => boolean | |
? `${string}` | |
: never; | |
// Params of a RouteDefinition {[keyof matchFilters]: `${any}`} | |
type RD$Params<RD extends RouteDefinition> = { | |
[k in NonNullable<keyof RD$matchFilters<RD>>]: MF$toString< | |
RD$matchFilters<RD>[k] | |
>; | |
}; | |
// Refine a defined RouteDefinition | |
type RD$RouteParams< | |
RD extends RouteDefinition, | |
tRD extends RouteDefinition = RouteDefinition<RD["path"]> | |
> = Refine<RD$Params<tRD>, RD$Params<RD>>; | |
/** | |
* @example | |
* ```ts | |
* const path = "/:aNumberParam"; | |
* type TRouteDefinition = RouteDefinition<typeof path>; | |
* | |
* type TParams = RouteDefinitionParams<TRouteDefinition>; | |
* // or with Refined param Type | |
* type TParams = RouteDefinitionParams<TRouteDefinition, { aNumberParam: `${number}` }>; | |
* const params = useParams<TParams>(); | |
* // ^? Type is { aNumberParam: `${number}` } | |
* ``` | |
*/ | |
export type RouteDefinitionParams< | |
RD extends RouteDefinition, | |
P extends Partial<RD$RouteParams<RD>> = NonNullable<unknown> | |
> = Prettify<Refine<RD$RouteParams<RD>, P>>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment