TypeScript Typeguard Transparency

Austin Mueller
JavaScript in Plain English
3 min readFeb 28, 2021

--

For those familiar with TypeScript, you may already know about typeguards and how useful they can be. If you are not familiar with typeguards, i’d recommend checking them out. There are plenty of guides and resources available on the topic:

While there are many great resources already available, most of the examples given deal with pretty small data structures or only deal with the validity of one field.

At my current place of employment, we use typeguards heavily to verify that API responses are what we expect them to be and that objects are of the right shape and have valid data before saving them into our database.

At this scale however, typeguards start to become too opaque for my liking… Especially when dealing with larger data models and nested structures.

Let’s look at an example of type Car and it’s subtypes of Engine and Wheel :

Now, what if the obj we are checking has an improper type nested in the wheel object? Or maybe the engine object has a fuelTypeof deisel rather than diesel because of some small typo somewhere in the codebase. While the typeguard will do its job, declaring that the object is not of type Car, trying to debug this situation will quickly become a nightmare. If you are throwing errors on failed type checks, you will likely see something like “Object x does not match the schema for Car”. At this point you will likely be thinking to yourself “uhhhh… yes it does, what the hell??”

The real pitfall here is that if one field is invalid, the whole check fails with no indication of why. This will then inevitably force you to start logging out the entire object so you can try to see where the invalid field(s) are… Not fun.

So, having encountered a number of these bugs in our system, I grew very frustrated with how little transparency our typeguards gave into the reasoning behind failed type checks. Following is the solution I came up with.

First, we have two generic helpers, RuleSet and isType.

RuleSet is a generic interface that defines a key and a function to check if that key is valid. The key in the rule set should match exactly to the key in the interface.

isType is a generic type check function that will take the object to check and the rule set to check against. The nice thing here is that isType will log out any key and value that fails the check.

Given our Car type example, we can change all of our typeguard functions (isCar , isEngine, isWheel ) to rule sets.

As you can see, our rule sets look almost exactly like our typeguard functions with the benefit of actually being easier to read and define (at least in my opinion).

Now if we have a small typo, like our engine model having fuelTypeas deisel instead of diesel we get a message that points out exactly why the object is invalid:

Conclusion

I hope you found this article valuable and that it saves you time debugging in the future!

Source code & tests can be found here: https://gist.github.com/armueller/ac31bfe290f256eda830ea495d3426dd

--

--

Entrepreneur, Real Estate Investor, Programmer. On a never ending quest for knowledge.