Skip to content

Instantly share code, notes, and snippets.

@sylvaindethier
Last active April 19, 2024 19:58
Show Gist options
  • Save sylvaindethier/daf830e610afce09360fff5506a328b1 to your computer and use it in GitHub Desktop.
Save sylvaindethier/daf830e610afce09360fff5506a328b1 to your computer and use it in GitHub Desktop.
SolidJS type safe for `useParam` from a `RouteDefinition`
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