Files
type-level-demo/08-type-programming/main.ts
T

31 lines
1.2 KiB
TypeScript

// This recursively matches path segments and parameters in segments which
// are surrounded by curly braces (i.e. `{foo}`) in these segments.
// The results are joined to a superset of objects with their params
// via the `&`.
// In example, the path /{foo}/{bar} results in the type
// `{ foo: string } & { bar: string }`
type ParseUrlParamsRaw<Url> = Url extends `${infer Start}/${infer Rest}`
? ParseUrlParamsRaw<Start> & ParseUrlParamsRaw<Rest>
: Url extends `{${infer Param}}`
? { [P in Param]: string }
: {};
// ParseUrlParamsRaw allows passing objects with any keys (i.e. `{name: "foo"}`) even though
// an empty object is expected (`{}`). This type constraint enforces this by checking if
// the result of ParseUrlParamsRaw<Url> is `{}`. `keyof {}` evaluates to `never`, so
// when `{}` is evaluated an empty Record is expected, otherwise the result of
// ParseUrlParamsRaw is expected.
type ParseUrlParams<Url> = keyof ParseUrlParamsRaw<Url> extends never
? Record<string, never>
: ParseUrlParamsRaw<Url>;
function navigate<P extends string>(path: P, params: ParseUrlParams<P>) {
// ...
}
function main() {
navigate("/hello", {});
navigate("/hello/{name}", { name: "foo" });
navigate("/book/{author}/{id}", { author: "foo", id: "bar" });
}