type ParseUrlParamsRaw = Url extends `${infer Start}/${infer Rest}` ? ParseUrlParamsRaw & ParseUrlParamsRaw : 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 is `{}`. `keyof {}` evaluates to `never`, so // when `{}` is evaluated an empty Record is expected, otherwise the result of // ParseUrlParamsRaw is expected. type ParseUrlParams = keyof ParseUrlParamsRaw extends never ? Record : ParseUrlParamsRaw; function navigate

(path: P, params: ParseUrlParams

) { // ... } function main() { navigate("/hello", {}); navigate("/hello/{name}", { name: "foo" }); }