Longing for Strong Types: Part I
I love strong types. I love having a compiler as my first line of defense against my human fallibility. Being able to ruthlessly refactor code with the knowledge that the compiler will be right there beside me throughout the entire process gives me the confidence to refactor freely. Not only that, but being able to encode business rules into the type system such that illegal states are unrepresentable makes it that much harder for bugs to creep their way into the system.
Conversely, I find myself less and less capable of tolerating weak type systems with each passing day. Writing code in a language with a weak type system makes me feel like I'm fighting an uphill battle to write correct code, especially knowing that there are languages out there that would aid, rather than hinder, me in my quest for correctness.
Strong vs Weak Types
There are a lot of definitions floating around for what it means for a programming language to be "strongly typed" or "weakly typed".
In some cases the distinction can be somewhat subjective:
Strong typing: A type system that I like and feel comfortable with
Weak typing: A type system that worries me, or makes me feel uncomfortable
Rather than formalizing everything and talking about type systems in the abstract, I will use concrete examples to describe what it is that I want out of a type system.
TypeScript
I use TypeScript almost every day at work. Building a React app, or any other type of JavaScript app, doesn't leave you with too many options in terms of language.
Having so much experience using TypeScript lends itself, naturally, to me having a lot of opinions on it.
The Good
For what it is—JavaScript with types bolted on top—I will admit that TypeScript is very good.
The compiler is fast, the tooling is great, and the speed at which the TypeScript team cranks out new features is astounding.
All of this is well and good, but there are many things that make me sad when I'm working with a TypeScript codebase.
The Bad
It's not surprising that TypeScript and I have a bit of a tumultuous relationship, given that one of its design non-goals directly contrasts what I want out of a type system:
- Apply a sound or "provably correct" type system. Instead, strike a balance between correctness and productivity.
Not having a sound type system is a non-starter for me as it just removes all of the confidence that I have in the system. The only thing worse than not having a static type system in the first place is having a static type system that I can't trust.
TypeScript does have some rudimentary constructs for things like sum types and pattern matching, but these are all a poor substitution for their counterparts in languages like Rust or F#.
The Ugly
One of the worst things about working with TypeScript has got to be managing typings for third-party JavaScript packages. Unfortunately this is just a reality of the JavaScript ecosystem. There are plenty of npm packages that don't have TypeScript typings or have poor or outdated typings. Many times I'll just opt to use a package untyped simply because the typings that exist are so poor that it makes working with the package a nightmare.
The typings problem is something that simply does not exist for a language where static types are first-class within the language. When I install a Rust crate, I don't have to worry about whether or not I will get reliable type information about that crate from the compiler.
Where do we go from here?
Now that I've aired some of my grievances about weak type systems, specifically TypeScript, what recourse do we have if we want strong types for frontend development?
I'll explore some potential options in the next post.