Using TypeScript to upgrade Ignite without losing my mind

Jamon Holmgren
Red Shift
Published in
5 min readMar 20, 2019

--

Big refactors are hard on developers.

When you have a code base that has grown over the course of three years and been worked on by dozens of developers, there are inevitably dusty corners, creaky hacks, and outdated dependencies.

We call that “technical debt” in the software industry. It’s a particularly apt term, because when it comes time to sweep out the corners and upgrade the dependencies, you’re in for a lot of work.

That’s the position we found ourselves in with Ignite CLI. It was created in 2016 to address a big need — providing a way to quickly spin up a React Native app with our boilerplate defaults — and had grown to be quite popular over the years. We released a new version in March of 2017 called Ignite 2 and switched from Yeoman to a brand new library we published called Gluegun.

Over the intervening years, Gluegun had moved forward but Ignite CLI was stuck on an early version, 0.20.0. This was an experimental pre-release version that was incompatible with the latest Gluegun. We really needed to bring it up to speed.

So, I was facing the Big Refactor. And I could tell it was going to be hard on me.

Enter TypeScript

Over the course of improving Gluegun, we had converted it to TypeScript. As a result, Gluegun now came with a robust set of type information published to NPM.

TypeScript’s type information, if you’re not familiar with it, allows the TypeScript typechecker to catch when you’re misusing a function or missing something important. If you’re using a code editor like Visual Studio Code, it also provides autocomplete information.

TypeScript seemed like the perfect tool to provide guardrails while I upgraded Gluegun (and other dependencies) to the most modern versions.

Converting to TypeScript

First, I installed TypeScript, added a build command, and renamed all the file extensions to *.ts. This immediately gave me “type inference”, which is where the TypeScript typechecker tries to infer what types I’m using.

This immediately caught a bug where we were missing an await on an async function.

It also found a couple other bugs, including one where we were using exitCodes.generic rather than the correct exitCodes.GENERIC.

TypeScript was paying off already!

Upgrading Gluegun

I then upgraded my dependencies to the latest Gluegun 3. With the additional type information, the typechecker immediately started spotting the problems that the backwards-incompatible upgrade brought with it.

I patiently went through each error, adding type information, changing code, and squashing those problems.

As an example, our interface for Gluegun commands used to require exporting an asynchronous function directly from the command file:

// in src/commands/mycommand.js
module.exports = async (toolbox) => {
// do stuff
}

In Gluegun 2+, we now required exporting an object so we can include additional information about each command, such as a description and aliases:

// in src/commands/mycommand.ts
import { IgniteToolbox } from '../../types'
module.exports = {
alias: 'mc',
description: 'Example command',
run: async (toolbox: IgniteToolbox) => {
// do stuff
}
}

In that code example, you’ll notice an imported type,IgniteToolbox. This was a TypeScript interface derived from the GluegunToolbox that comes with Gluegun. The benefit there was all my commands could be typechecked as well! This uncovered additional bugs.

Screenshot of IgniteToolbox type information

Each step of the way, I uncovered more and more of the dusty corners and subtle bugs and was able to sweep those away without losing my mind.

TypeScript and Gluegun conversion done … now what?

Once everything was building, I knew that part of the job was done. But typechecking only covers some classes of bugs. This is where our test coverage in Ignite came in handy.

I was able to first run the unit tests and make sure they all passed, and then run through our end-to-end integration tests.

Integration tests are slow (we literally spin up a full React Native app with Ignite and see if its tests pass), so this part was the most tedious. I even found some ways to keep my computer from slowing to a crawl while the integration tests were running.

After four evenings of effort, I had the tests passing!

Was it worth it?

On Twitter, Jess Telford asked if I felt the conversion was worth it. After all, the upshot was that I ended up more or less where I started prior to the conversion, at least from an end-user perspective.

I can say confidently that the effort was well worth it, for a variety of reasons:

  1. I learned a ton personally and have more confidence in my ability to convert a JS project to TypeScript
  2. Getting Ignite CLI onto the latest version of Gluegun serves two purposes — we can use the nice new features of Gluegun, and we can also test new Gluegun features on a nontrivial code base
  3. We’ve been having a lot of subtle bugs with Ignite lately and TypeScript is a great tool in the fight against those
  4. Utilizing TypeScript will improve stability and make new features and refactors easier in the future

And, most importantly, converting to TypeScript prior to doing a large refactor made a huge difference in preserving my sanity.

Resources

If you’re interested in learning more about Ignite, Gluegun, integration testing, or TypeScript, check out these resources:

Also make sure to subscribe to this Medium publication, Red Shift, for more TypeScript, React, React Native, Gluegun, remote work, design, and more articles!

If you’re looking for software design and development, especially in mobile/web UI/UX design, React Native, React, Node, and other JavaScript-centric technologies, check out my consulting studio Infinite Red.

Jamon Holmgren is a co-founder and CTO at Infinite Red, an app and web design/development agency. Follow Jamon on Medium, Twitter, and Github for more Node/Gluegun, technology, and business talk. If you’re near Portland, Oregon, let’s have coffee!

--

--

Co-founder & CTO @infinite_red. Lutheran, husband, dad to 4, React Native Radio podcast host, Twitch streamer, hockey goalie. Talking shop!