Leveraging Conditional Properties Type Checking with Discriminated Unions
Tags: typescript, codequality, codenewbie
Published: 2023-09-04 20:49:48

Strict type checking feels like a privilege ever since I encountered it in my career and i'll give anything to have it in any part of my code. So i decided to write this short post (for strict type checking fandom😌) to share a trick for type checking conditional properties of an entity . 

I am just going to go straight to stating the problem so you can better understand what we are dealing with, (actually Its because I fear boring you more than anything). And also to stick with the goal; keep the post short.

## Problem statement

The first thing I'll like to note is, this example is super contrived and there are better ways to go about this in a real world scenario. It however complements what we are trying to achieve so, here we go.


![Create user props type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93ydh70fmdykf0mexb13.png)

In our application, we have a `createUser` function that takes the properties of a user as described in the `CreateUserAttr` type. We have two types of users (Admin & User). The two variations of `CreateUserAttr` have some shared props and differences which are marked as optional props of the `CreateUserAttr` because we want to pass admin only props when creating an admin and vice versa. Hence the problem; the role prop when creating a user type of `Admin` is not optional (as seen below), we have to provide role and all other props related to the `Admin` type (super contrived 😉) likewise the verified prop when creating a user type of `User`.

![function call without strict type checking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o1xr7huikh3ia6oem7g2.png)

## Solution 😋

The solution is to take advantage of typescript's union and intersection types to create a type known as **discriminated union** or tagged unions. We first create our base type which has all the shared props between the `User` & `Admin` type. Then we intersect it with a block that has two union type where we'll add the admin props and the user props. Putting everything together we have a type that has our shared props AND either the `Admin` props OR the `User` props.

![discriminated union or tagged union in typescript](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeDJzcTlrNHA3YXB0YWRiOGlvc3hpa3F5MTB4dDg1dnl3amxwajFxeCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/jWlncszJpP6WMoHAMg/giphy.gif)

Now we get the yelling when we don't pass the all the required props relating to the type of `User` we are creating, SWEET!

![Create admin with missing prop](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a419so0lfiilsdlscede.png)

Hooray🎉! we now have type checking to make sure we are passing the required props of the user type being created, and also check that we don't pass a prop that doesn't belong. The example we have explored is intentionally simple to illustrate the core concept, but in real-world applications, the benefits become even more apparent. You can explore more examples [here](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions).

![function call with type checking on discriminated union](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ocbay81uqxas5ttfox1x.png)

## Conclusion

In the world of software development, the privilege of strict type checking is indeed a treasure for those who have experienced its benefits. The added advantage of this approach is, our code becomes more readable (it's clear which property belongs to which union) and autocompletion (depending on the IDE). Our Types also become easy to extend because adding new types to the union is straightforward. So, whether you're tackling simple examples or navigating the complexities of real-world applications, discriminated unions offer a potent tool to make your code more robust, reliable, and enjoyable to work with. 

And don't hesitate to drop a comment if you have any specific topics you'd like me to cover in future content❤️.