Watch a 5 Minute Demo

What is your company email address?
What is your country/permanent residence?
Privacy Policy - By signing up, I agree with Iterable's Privacy Policy. I understand that I am signing up to Iterable Marketing emails and I can unsubscribe at any time.
Form footer image
Loading...
What is your first name?
What is your last name?
What is your company email address?
What is your company's name?
What is your country/permanent residence?
In which state do you live?
Privacy Policy - By signing up, I agree with Iterable's Privacy Policy. I understand that I am signing up to Iterable Marketing emails and I can unsubscribe at any time.

Schedule a demo to learn more.

What is your country/permanent residence?
Privacy Policy - By signing up, I agree with Iterable's Privacy Policy. I understand that I am signing up to Iterable Marketing emails and I can unsubscribe at any time.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Form footer image
Thank you !

Thanks for contacting us, we’ll be in touch shortly.

Narrowing Union Types With Typescript Predicates and Assertions

Imagine making an API request to an endpoint that provides data organized into folders. Requesting the data from a specific folder would return items that the folder contains as well as additional folders nested directly under the folder in question.

A folder navigation UI would need to split apart the Folders from the Items in order to render properly. This could easily be solved in a component by using filter to get rid of the undesired items at the point of use but at the cost of iterating the set for each use.

Also, what if I needed to split other kinds of arrays? For example, what if I had a list of users with an isActive property, and I wanted to split those into [activeUsers, inactiveUsers]? The underlying problem is pretty general so a small utility function would help here.

Problem: Given an array of two kinds of things (T and U), let’s split them apart and return each subsection of the array in a tuple ([T[], U[]]).

The functions works, but the compiler can’t actually tell if the splitFn is narrowing the types correctly so it complains. There are two primary ways to provide it additional context.

Solution 1: Type Assertions

The most obvious solution is just to tell the compiler what’s happening via a type assertion. These assertions are done using the as keyword.

Generally, type assertions are discouraged as they can easily reduce the effectiveness of the compiler by overriding it erroneously. That’s true here as well: providing a splitFn that doesn’t accurately or exhaustively split the types will cause problems downstream.

Solution 2: Type Predicates

In programming, a “predicate” is a function with a single parameter that returns either true or false. Typescript uses predicates to narrow types. It’s annotated with a special return description using the is keyword.

The splitFn above is already functioning as predicate, so updating the type definition will clear the error and narrow the type for the compiler.

This clears the error as well, but there are a couple of caveats:

Caveat 1: This approach makes more sense when splitting apart an array into two strongly differentiated subtypes represented in the type system. In the second example (the active users) the following works, but the predicate function has a hard time describing the intent when narrowing within a type:

Here it would probably be better to create ActiveUser and InactiveUser types. However, certain subgroupings resist type descriptions. There’s no easy way that I know of to break apart a list of users into newUsers and oldUsers where the differentiator is a test for if an accountCreated timestamp happens before or after a specific time.

Caveat 2: While the type safety here is stronger than in solution 1, there’s still no guarantee that the predicate is error-free in its differentiation logic. A poorly constructed predicate function could still result in compiler errors downstream if it fails to accurately differentiate between the types.

Conclusion

As neither of these two approaches guarantees type safety, I haven’t arrived at a strong conclusion for recommending use. I’ve opted for using type assertions given that it feels more like idiomatic Javascript to me and there’s less pressure to create new types just to satisfy the compiler when operating on single-type arrays. In less generalized utilities, I’d probably opt for predicates. Which style do you use?

Search Posts

Talk to your CSM

Please provide your company email
Privacy Policy - By signing up, I agree with Iterable's Privacy Policy. I understand that I am signing up to Iterable Marketing emails and I can unsubscribe at any time.

Welcome Back!

Loading...

Thank you!

Thank you for contacting us, we'll be in touch shortly.