Post

Embracing Static Typing with TypeScript

Why TypeScript?

My capstone project is a privacy-sensitive, email provider-agnostic, web-based mail merge solution. Because we’re building a web application, it’s pretty much impossible to get away from the JavaScript ecosystem. Furthermore, to protect the privacy of our application’s users, we made a design decision to keep most of the data processing on the client side. Our intention was to minimize the amount of data that leaves our users’ machines. It made sense to us–we can’t possibly leak user data if we never had it in the first place. We needed a language that is more robust than JavaScript, and at the same time it has to be able to run in the browser. TypeScript emerged as the best option.

Benefits of TypeScript

Static typing

JavaScript has so many pitfalls that my Web Development class literally had a module dedicated to them. Some of the most memorable examples I saw were the result of JavaScript going wild with type coercion.

Example:

1
2
3
4
5
6
// add or concatenate
function add(a, b) {
    return a + b;
}
add(1, 2) // equals 3
add(1, "2") // equal "12"

The ability to explicitly specify types in TypeScript mitigates some of the more puzzling runtime errors caused by accidental type coercion. Take the add function from earlier for example, we can explicitly tell the compiler that it’s supposed to add two numbers together and return a number.

1
2
3
4
function add(a: number, b: number): number {
    return a + b;
}
add(1, "2") // the compiler will catch this

Readability and maintainability

Thankfully, I’m not working on the capstone project alone; I have a partner who is a collaborative, skilled backend developer based on the East Coast.
As I write code for the project, I constantly remind myself that someone else has to read it and understand it enough to extend my work. I find that a static type system allows me to better express the “why” behind my code, because I can–for example–communicate what a function does by imposing constraints on what it cannot do.
The following example is a real use case that came up as we worked on the project. We’re using a React state hook to track the value of some radio buttons.

Example:

1
2
3
4
5
6
7
8
9
// pure JavaScript
const [nullOption, setNullOption] = useState("ignore"); 

// same thing in TypeScript
enum NullOptionEnum {
    Ignore,
    Replace
}
const [nullOption, setNullOption] = useState<NullOptionEnum>(NullOptionEnum.Ignore);

Not only is the code’s intention more clear, we can also count on the IDE to give us some useful hints about NullOptions

The Cost of Adopting TypeScript

Like every design choice, there are trade-offs associated with our decision to use TypeScript. For our project, the benefits outweigh the costs, so this section will be relatively short.

Increase in complexity

All the nice type checking and IDE support provided by TypeScript come at a cost. First of all, there’s the increase in the amount of time spent on getting the team up to speed on a new language. Both me and my partner have experience with building web apps using JavaScript, so the learning curve was not so steep, but it was not negligible either. Then we have to incur added complexity in the code: function signatures are longer, type definitions may have to be exported, and some error messages are harder to interpret.

Potential improvements

I’m not going to pretend to know TypeScript well enough to suggest any improvements for the language. I know programming language design is an involved process, and it’s a topic that is way above my pay grade.

This post is licensed under CC BY 4.0 by the author.