More posts by Gordon Wallace

Recap

In part 1 of this series we developed an API for searching and sorting HTML tables. We introduced some code reuse in part 2 but were dissatisfied with the boilerplate. Part 3 teased us with keyof, a promising feature of the type system. In this final post we dig in and finally deliver the goods.

 

Mapped, Conditional Types

Mapped Types

TypeScript provides the ability to transform a type into another type using a mapping operation. Imagine we want to create a version of our Person type where all the fields are read-only:

With that, you can tell the compiler to take an instance of the Person class

And treat it as though it was written this way:

Pretty cool trick if you decide to introduce immutability to an existing API, for example.

 

Conditional Types

Conditions can be applied to types during mapping. Here we create a type that contains only the string fields on Person:

That says map k -> k if Person[k] is a string, otherwise map k -> never, which is a type that matches nothing.

Effectively eliminating 'age' from the union. This is exactly what we need.

 

The Final Version

API Modifications

First we add some type aliases that are easier to digest than the raw definitions.

And fix the hole in the TableSort API.

Application Code

The compiler now prevents us from doing the wrong thing and code completion guides us to the right thing. Adding the string columns

And the number column

Yields this table configuration code

And this code refreshes the display

Beautiful! No boilerplate. It looks almost exactly like JavaScript, but has all the power of compiler-enforced type constraints behind it. We can build and refactor on this foundation with confidence, move faster, and break less things.

 

TypeScript – A Good Part

Okay, TypeScript has a lot of great parts. Like JavaScript, the language is an expressive joy. Unlike JavaScript, it is not a waking nightmare when the project grows beyond 100 lines of code1.

It should be noted that our team has struggled with quirks arising from JavaScript compatibility. While backward compatibility is a smart strategic step for language designers, if you’re a developer starting a new project, turn off the compatibility features. In areas of our codebase where we are deep in pure TypeScript, it is a true pleasure. Around the edges where it’s still JavaScripty, we still spin our wheels, scratch our heads, and roll the dice.

 

How Excellence Happens

My teammate Patrick and I never set out to solve this problem together. We were independently driven to rid the code of some repetitive boilerplate. Along the way, we discussed some ideas and agreed that, while our respective ideas had merit, none were satisfying. We kept up our other work, but poked at this problem along the way. And we came up with something better than either of us could have done alone. We both discovered something new, and I had a $5 beer. For me, this kind of experience brings the most joy, and I am grateful to Patrick for having the enthusiasm and desire to challenge my work, to make our software better, together.