“I love Typescript, but I hate the configuration”

Back upright, posture checked, fingers have just been cracked, you’ve got some good music playing and you’re ready to sit down and start coding on your new cool idea.

Whilst ideas begin to unfold in your mind and you’re about to burst with creativity you envision “hey, this would be a cool opportunity to get better at Typescript” and you decide to roll with that for the project.

Everything is fantastic! Until…

Let’s be honest, you barely got anywhere before you met the first demand for a configuration file. Briefly after installing typescript you need to setup a tsconfig.json file in order to get rolling.

Alright it took a while but you dealt with it. Now back to coding and letting creativity flow!

“Ah! But linting and formatting! Of course!” you think. And while adding prettier and eslint are both seemingly small tasks, each of those require yet another configuration file.

You decide that you want a faster experience in development and opt for ts-node to compile and reload your development server, but, you guessed it, that requires yet another set of configurations.

“Ugh. Well, at least I’ve got my environment running and things are working” you think to yourself. And you’re correct!

… That is, until you decide to add testing to the mix. After some thorough research you settle on a well-known library jest but that doesn’t play well with Typescript or ESM.

“UGH! Enough! I’ve had it with these goddamn configurations in this goddamn editor!” and you toss the project, giving up on working with Typescript for your cool new project.

Don’t worry, I’ve got just the fix for you today 🙂

“Config hell” is the modern day tax of Typescript developers

Unfortunately the above scenario is way too common in the Typescript ecosystem. I was prompted to think about this quite a bit after seeing somebody state “I love Typescript, but I hate the configuration” on reddit and after pondering for a while, I totally understood.

I thought it was a rather profound quote, because it saddens me that an otherwise awesome language gets boggled down in configuration and ‘getting started’.

Now, thankfully, configuration isn’t something I struggle with very often but especially to someone newer or someone who likes to create side projects a lot, this struggle can be very very real.

What I’ve seen most developers end up with, is that they somehow set the own defaults, configuring their favorite libraries and shipping them to a Github repo where they can easily find and reuse the configurations.

Another path entirely to take would be to skip the configuration part by using deno, but for now that comes with its own set of caveats.

The core issue is that all these tools offer configuration options in the first place, because Javascript (and by extension, Typescript) developers are used to having the option to configure everything.

But having options, also means there are many more variables to navigate and I personally believe that some convention over configuration is preferable.

It doesn’t help that Javascript is moving towards a different style of imports. Sure it’s going to be better in the long run because we’re going to have one import style that’ll work across environments, but the transition period is long and tedious.

In my blog post about moving from CJS to ESM I describe how that process has its own host of caveats, but if you don’t believe me, simply try enabling "type": "module" in your package.json and get ready to spend at least a few hours debugging.

Light at the end of the tunnel, JS is maturing

Thankfully, Typescript and its tooling gets better every week. It seems to me that many library and framework developers have identified this as an issue and are working actively towards minimizing the amount of configuration.

Take prettier and eslint for example. Both allow a ton of configuration for specializing but argue that people shouldn’t configure it (at least too much) and instead rely on sensible defaults.

It seems like this point of view is slowly solidifying itself in the Javascript and Typescript ecosystem, making it slowly move towards a more uniform state.

It’s long been a point of criticism and a big reason for why Javascript is considered immature, that it’s very much “gunslinger land” and no two projects are alike – at all.

Moving towards sensible defaults and zero-configuration with libraries providing support for most use-cases out of the box is a big step in the right direction.

vitest for example are making a lot of strides towards setting up testing environments for an express backend that doesn’t come with many of the same caveat that the older libraries introduce.

It seems to me that the growing maturity in the Javascript/Typescript ecosystem is putting us on a path where developers can spend their time and passion developing, and much less time configuring.

Frameworks such as remix and NextJS are really making strides in Developer Experience, making it as easy as possible, to do as much as possible.

So how do you fix your current situation?

While it’s all good and dandy that the future holds promises, it does not do much for your current situation does it?

I’m going to present three simple solutions for you right here, briefly describing each:

  1. Use deno instead of node. Because Deno is built specifically to fix some of the issues that NodeJS has (by the same original author, Ryan Dahl, too!), it offers a lot of convention over configuration. Bear in mind that you are trading that convention with other caveats, but it is much easier to get going.
  2. Create and maintain your own list of preferences. This one is probably going to give you the best result because you get to know your configuration by heart and can re-use it across multiple projects as you deem fit. However, it does take some time staying on top of ever-evolving standards and config options.
  3. Lastly, my favorite option: replace libraries with ones that offer more out of the box, with a higher focus on DX. Granted, this option is a bit of a risky play as it removes one of the things we are so used to in the JS ecosystem: configuration. But if that’s OK with you and you want a modern environment with good DX read on.

As somebody who highly values Developer Experience, almost at the expense of anything else, I spend a good portion of my time ensuring that our repositories are set up with the best tools possible to maximize productivity, while minimizing configuration and time spent discussing things that don’t really matter (spaces vs. tabs anyone?).

As such I’ve been trying to replace a ton of our existing libraries and here are a list of my recommendations

Rome

Rome replaces eslint and prettier and does so without a single configuration file.

Well, to be fair it does have a configuration file but its a lot simpler and the developers goal is to remove a bunch of the options we’re used to from the previous libraries.

At first I was hesitant because it isn’t fully developed yet and doesn’t provide as rounded support as the combination of eslint and prettier does, but I switched several months ago and haven’t looked back.

Vitest

Vitest replaces any former unit testing libraries you may have. In our case we were running a combination of testing libraries, most predominantly jest and supertest. This is still new enough that I don’t have a full overview of the results, but initially this replacement looks very promising!

Vitest is incredibly fast too.

Ts-node-dev

This is one of my favorite libraries (something I talk about as often as I can) and one I feel doesn’t get mentioned enough. Instead of using various combinations of nodemon, ts-node, concurrently, tsx and a handful more, ts-node-dev simply handles it, and it does so elegantly and quickly.

It’s incredibly fast and reduces the amount of libraries you need to run for things to just work.

That’s it for now!

I hope the above helped and you got unstuck or at least pointed in the right direction.

So excited for the future of Typescript!