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.
About The Author: Andrea Ramirez
More posts by Andrea Ramirez