Pattern matching is a declarative code-branching technique that manages multiple states without relying on imperative constructs like if
, else
, or switch
. While widely implemented in languages well-supported for functional programming, such as Swift, Haskell, and Rust, it is still in the early stages of consideration for addition to EcmaScript (as of 2023).
Fortunately, the library ts-pattern provides a means to implement pattern matching with features like type-safety, exhaustiveness checking, and more.
In this note, we are going to demonstrate how you can integrate pattern matching into your standard state management code in React JSX components.
π΄ Ternaries
Letβs examine how we typically implement a component with an API response. Ternaries are undoubtedly a default approach that a React developer would use when handling various statuses from an API response.
However, there are two drawbacks to the approach of using ternaries:
- It can be messy, especially when dealing with nested conditions.
- It cannot accommodate all cases when new states or conditions are added.
π Switch + IIFE + Exhaustiveness Checking
We can use a switch
statement to make the code look cleaner, but it will always require the use of an IIFE (Immediately Invoked Function Expression) since a switch statement is not an expression that can be used directly in JSX in React.
Additionally, we can create a function named safeGuard
, which takes an argument with the type never
. This ensures that the argument passed to the default branch of the switch
statement has been narrowed down to never
, indicating that we have handled all possible status
values from the API response.
π’ ts-pattern
ts-pattern
provides a match
expression function to match all possibilities from the input without requiring an IIFE. It returns a builder for us to add our pattern matching cases.
.with()
takes two arguments. The first one is the expected pattern for this branch, and the second one is a handler function with parameters narrowed down to what matches the specified pattern..exhaustive()
is the final function in the builder that executes thematch
expression and returns the result from the matching branch. It also provides exhaustiveness checking to ensure that all cases from the input have been handled.
The exhaustiveness checking of
.exhaustive()
This checking occurs at compile time and comes with a trade-off in terms of performance, as the type checker needs to perform additional work.
An alternative to
.exhaustive()
You can use
.otherwise()
instead to execute the expression without exhaustiveness checking. It takes a handler function to return if none of the cases are matched.
You can explore more details about ts-pattern
in the documentation, including information on different data structures for pattern matching, advanced methods for pattern matching with .with()
, type inference, and more.
References