Type safe URL templates
31 October 2024
Updated: 01 November 2024
I don’t think the below is a good idea, but I thought it was a fun little Typescript excercise so here you go
Something I see as a sort of recurring pattern is people doing template replacements in URLs, particularly something like this:
Now, while you can more simply make this template a function like so:
For some reason people don’t like to do that, I think it’s that it looks like a lot of duplication and perhaps they just don’t believe in making their own functions (who am I to know)
More often than I’d like, I instead see people doing this:
I think this is horrible, and while it works, it’s quite brittle and unless it’s got some tests or other means of verifying that this works it’s a really quick source of bugs for when things change, for example if you add or remove a parameter from the URL
Now, I’m a huge fan of using the compiler to derive as much correctness as possible, even before even really thinking about tests, so when looking at something like this all I can think is that the TypeScript compiler can solve this
I’m not saying that you should do what I’m about to present here, I think you’re probably better off using the method of a template being a simple function above, but I think it’s a fun little mental excercise
The Typescriptssss
Firstly, we need to parse this URL template things, this is simple enough using a little recursive type:
Next, since in a URL all keys need to be a string at the end, we can define an object for URLs that uses this to define a record:
A little test of the above types shows us:
So great, that works and we can use that as the basis for building up more general function for creating these “safe urls”
Before we go there though, let’s take a moment to think about the result of this URL. Now we could return the URL as a string
but since we’re being strict it would be nice to inform downstream consumers of what this string is made up of. Using a very similar method to how the PathKeys
type was done we can create a type for the result of a URL where the params have been replaced
Lastly, we can define the implementation of the URL replacer which is effectively just calling the String.replace
method on an input URL while using a strong type for the URL:
Using this looks like so:
Now, is this better than random string replacements? Yes. Is it better than just using string interpolation? No.
While this isn’t something I recommend using I think the idea about thinking of underlying structure of common data types is important and something that’s worth thinking about in order to define more complete solutions
Lastly, if you’re into this type of thing, take a look at some of my other TS shenanigans: