How to Refactor a Null Checking Condition With Object Composition in TypeScript

Using the Introduce Special Case pattern to refactor a null checking condition

Itchimonji
CP Massive Programming
5 min readDec 6, 2020

--

Early in my career, the most commonly occurring condition in TypeScript and Angular was the null checking condition. I think other developers have the same habit of making their code more stable. Such a condition isn’t bad, but when we check the same special value every time in different places in our code, we may have redundant code and we may not be complying with the DRY principle.

A null checking condition is nothing more than a check for data availability:

Weather Service

It is helpful to implement a Special Case object to handle such conditions when most or all of them have the same reaction to the particular value. We could separate that reaction to a single place.

In TypeScript, there are two different innate null checking conditions (and maybe more). One checks for null, while the other one checks for null and undefined:

There are some JavaScript helper libraries like lodash that we can use to shorten the conditions:

isNull & isNil

What Is a Special Case Object?

“[…] this is the Special Case pattern where I create a special-case element that captures all the common behaviour. This allows me to replace most of the special-case checks with simple calls. […] If I need more behaviour than simple values, I can create a special object with methods for all the common behaviour. […]” — Martin Fowler, Refactoring

In simple words, regarding the null checking condition, we create a special object for the cases when a value or an object can be null so that a default class with common behaviour will be returned.

If we like object composition and our code is architected by many classes and interfaces, we will love this pattern.

The Problem With Null Checking Conditions

Firstly, we need to comb our code or rather related object families for null checking conditions. After that, we need to scan these conditions for the same reactions. If it shows the same behaviour, we are lucky and we can use the Introduce Special Case pattern for this.

In the following example, we can see how I handle null in the same way but with different context data in the else-sector. But the content is the same — there is no data:

Weather Service

There could be more places with no-weather-checks in the code base.

In my example, I need to implement only three attributes: title, state, and temperature. So if we have many places where a check against null is required and we need to assign many attributes with special values, this maintenance effort to change the code could be very high.

In summary, this approach is not DRY and the maintenance effort is draining when we need to modify or extend the assignment of the attributes.

Modules that contain null checking conditions.
Modules that contain null checking conditions.

The Power of Object Composition

In many cases, we have classes, interfaces, or other modules to represent a business case. In the example above, we have a weather class or a weather interface and fill the weather object with user-readable data to explain that there is or is no data.

Code against interfaces
Code against interfaces

So we have got nice class architecture conditions to use a Special Case object to refactor and to remove repeated null checking conditions by expanding the model with a No-Weather-Data class (or Null-Weather-Data class). I explicitly wrote “expand” because we’re complying with the Open-Closed-Principle. If there is not a nice class architecture, we need to refactor our code to be more polymorphic. Refactoring by Martin Fowler is an instructive book that shows we how to accomplish this.

UML to refactor a null checking condition
UML to refactor a null checking condition

In this case, we do not need to modify the data of the WeatherData object at runtime and we can make the attributes immutable.

Expanded with immutable data
Expanded with immutable data

In the NullWeatherData class, we can assign fixed values for the attributes (e.g. Unknown or Unknown weather as the title). Whenever we need to change these values in the future, we will change them in one place only: inside the NullWeatherData class. We will no longer need to comb the entire code base:

WeatherData class

Now we carry on and look at the code base to see if we can replace the previous return data logic inside the service (or in other places) with the polymorphic behaviour:

Weather Service

Wherever we request a weather object, we need to make sure to check the data before returning an object.

We comply with the Open-Closed-Principle with a simple null object, which is a special case of the Special Case pattern. We have got only one place where the NullWeatherData is created and maintained. Its only responsibility is to return the context that there is no weather data and show this to the user. That is all.

Conclusion

With the Special Case pattern, especially the null object pattern in this example, we can refactor null checking conditions to comply with the Open-Closed-Principle.

Thanks for reading! Follow me on Medium, or Twitter, or subscribe here on Medium to read more about DevOps, Agile & Development Principles, Angular, and other useful stuff. Happy Coding! :)

--

--

Itchimonji
CP Massive Programming

Freelancer | Site Reliability Engineer (DevOps) / Kubernetes (CKAD) | Full Stack Software Engineer | https://patrick-eichler.com/links