balazsbotond / urlcat

A URL builder library for JavaScript.
https://urlcat.org
MIT License
1.82k stars 57 forks source link

Infer required params from path template #138

Open mdarens opened 3 years ago

mdarens commented 3 years ago

Is your feature request related to a problem? Please describe.

When param keys are defined in the path template, there is no static validation that they are present in the params object, which can lead to unbound path template components at runtime. The most likely case in which this occurs tends to be a casing or spelling mismatch on a path template component or params property.

Describe the solution you'd like

With the advent of template literal types in TypeScript 4.1, we can check for keys defined in the path argument and make them required in params. I've stubbed out a naïve annotation, which doesn't cover all the implemented overloads, as a proof of concept.:

type PathTemplateTokens<Rte> = Rte extends `${string}/:${infer P}/${infer Rest}`
  ? P | PathTemplateTokens<`/${Rest}`>
  : Rte extends `${string}/:${infer P}`
  ? P
  : never;

function urlcat<BaseUrl extends string,
  PathTemplate extends string,
  Params extends Record<PathTemplateTokens<PathTemplate>, string | number>
>(
  baseUrl: BaseUrl,
  pathTemplate: PathTemplate,
  params: Params) { };

urlcat("http://example.com", "org/:orgName/user/:userId", {
  foo: "bar"
}); // error! required properties missing
urlcat("http://example.com", "org/:orgName/user/:userId", {
  orgname: "SprocketCo",
  userId: "951d20a0-e188-4db4-a946-df426d3d9e91"
}); // error! suggests `orgName` when spelled `orgname`
urlcat("http://example.com", "org/:orgName/user/:userId", {
  orgName: "SprocketCo",
  userId: "951d20a0-e188-4db4-a946-df426d3d9e91"
}); // ok, everything required is there
urlcat("http://example.com", "org/:orgName/user/:userId", {
  orgName: "SprocketCo",
  userId: "951d20a0-e188-4db4-a946-df426d3d9e91",
  sortBy: "sprocketsPerWeek"
}); // ok, extra stuff goes in the query string

Describe alternatives you've considered

N/A

Additional context Add any other context or screenshots about the feature request here. TypeScript Playground link to stub implementation

Screenshot of TypeScript suggesting params: image

Screenshot of TypeScript catching a spelling mistake: image

Thanks so much for your time! I love this library.

mdarens commented 3 years ago

I went ahead and added the overloads. see this gist

balazsbotond commented 2 years ago

Wow, this is amazing. If you are still interested in this feature, please make a PR.