Bringing the type safety in your TypeScript projects to a whole new level.

Photo by Florian Klauer on Unsplash

I can’t imagine starting a serious project using only plain old JavaScript. With all my respect to the de facto lingua franca of the web, I’ve been totally sold to TypeScript for several months now. Turns out, there are ways to take this relationship to a whole new level.

What is TypeScript?

If you haven’t had a chance to stumble upon TypeScript yet, I’ve got great news for you: complain no more about this dreaded, dynamic nature of JavaScript. We’ve got you covered now, and we’ve got static types. More and more libraries and frameworks come with their *.ts counterparts every day. Gone are the times of functions in the wild accepting any kind of arguments. How great is this:

or this:

Yeah. You can finally be sure what goes in and what comes out.

There are other competitors on the market, too. Flow by Facebook is one of them.

How does static typing help in everyday work?

If you come from languages like Java or C#, I think the answer will be obvious. Not only it is easier to understand the input and output of certain APIs, but you also gain a great buddy — the type-checking compiler! TypeScript’s one is pretty flexible when it comes to the configuration, so while it’s best to adhere to the strict laws, some rules can be relaxed (if needed). On the other hand, you get strong IDE support — be it Visual Studio Code or Webstorm, with all their code navigation, completion & refactoring features.

And should I mention that Everything You Do Is Now Much, Much Safer™?

The problem with null values

Typed language, however, cannot save you from the pitfalls of null values. JavaScript made the situation even stranger — by introducing undefined , which behavior isn’t much different than null 's. There are few niceties in TypeScript that make working with such values safer, eg strictNullChecks , which is a compiler setting that enforces strict, explicit checks against a possible absence of a value. Such that if you want to allow the value to be null or undefined , you would have to write:

Notice that this code will not compile — the age field was declared with a ? at the end, meaning that it can be a number , but it can also be undefined ! TypeScript will yell at the last line, saying that you’re trying to access a possibly non-existent value.

The solution to such situations has always been called a defensive style programming:

This line compiles, but we had to introduce an explicit check, with a fallback to some default value in case of age being undefined (or null ). Few more lines like this and the code becomes clumsy pretty quickly.

There are, however, better ways to deal with such… optional values. Languages like Haskell, Scala, ML & Rust have almost always had a solution for that. They introduced the notion of the wrapper boxes called Option , Optional or Maybe . Such “boxes”:

are generic over the value they encompass, like Maybe<T> : Maybe<number> or Maybe<Person> ,

: or , enumerate two variants: Just<T> & Nothing (or Some<T> & None — these are alternative names)

& (or & — these are alternative names) expose an elegant interface of methods to modify or safely “unpack” the possibly contained value.

Using TypeScript, such features can be easily integrated with true-myth, which is a “library for safe, idiomatic null and error handling in TypeScript” (more about error handling later). An example above, rewritten with true-myth would look like that:

Notice the use of Maybe<number> in the type declaration — that way we declared, that the value might be there, but it is equally possible that it’s absent, while in the following lines we will never have to mention either of these ugly words starting with “un…” or “nu…” at all!

In order to calculate the value of twiceTheAge , we map over the age , providing an arrow function that is called only if the value was there. We want to pull out the value from the box at the end of the day, but hey — remember, that it could not be there — hence a fallback value of 0 is passed to the unwrapOr call.

No if s, ternary operators and all this defensive style — just a nice, functional flow. Can you guess the final value of twiceTheAge ?

The problem with errors

Turns out — not only an absence of value can be handled in a much better, idiomatic way, but so can application errors! Let’s admit it — a classic error throwing pattern (unwinding the stack if not caught properly) should rather be avoided, and if really needed — pushed to the “boundaries” of the application, in scenarios of where it no longer makes sense to recover from a fatal exception.

So, having a function that can possibly throw an error (notice the use of never type denoting a function that throws an exception and thus never returns):

And having learned about the Maybe<T> type, we could write:

Well, not so fast! This way we possibly lose insight into why the function failed to return a value in the first place.

Enter Result<T, E> . Also known as Either in other languages (Haskell, Scala), it’s another type of a “box”, although this one is never empty! It holds… well… a result of some computation, be it a target value, or a value representing an error. So instead of losing the trace of what possibly went wrong, we can return an Err containing eg. an error code:

A Result exposes a very similar API to the Maybe type — you can map , flatMap and unwrapOr (amongst many others).

Summary

Safe optional values, errors represented as data — this is what I call the higher order of types. These patterns are very well known in other (mostly functional) languages, but it seems to me that the world around JavaScript has yet to discover them.

My team and I have been using both described types in at least two serious, commercial projects so far. You won’t see the defensive style against null or undefined in the codebase, as well as errors unwinding a very high call stack.

We even came up with some patterns for React components, but this, as well as other details and use cases, is an interesting topic that I want to write about in another blog post.