DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Pattern Matching in Typescript
Stefano Regosa
Stefano Regosa

Posted on • Updated on

Pattern Matching in Typescript

What is Pattern Matching?

Pattern Matching is a declarative much more powerful and less verbose alternative to imperatives "if/else" conditions.


A definition can be found inside Scala Documentation

β€œPattern matching tests whether a given value (or sequence of values) has the shape defined by a pattern, and, if it does, binds the variables in the pattern to the corresponding components of the value (or sequence of values).”



In Functional Programming languages, there're built-in keywords for Pattern Matching. Typescript though is one language that works very well with Functional Programming but lacks this feature, for this reason I made a package pattern-matching-ts that aims to bring Pattern Matching feature to Typescript through Discriminated Union Types / Algebraic Data Types.

Pattern Matching with Option

What's an Option Monad?

"In programming languages (more so functional programming languages) and type theory, an option type or maybe type is a polymorphic type that represents an encapsulation of an optional value; e.g., it is used as the return type of functions which may or may not return a meaningful value when they are applied. It consists of a constructor which either is empty (often named None or Nothing), or which encapsulates the original data type A (often written Just A or Some A)."

Let's implement our Option Type signature

interface None {
  readonly _tag: 'None'
}

interface Some<A> {
  readonly _tag: 'Some'
  readonly value: A
}

type Option<A> = None | Some<A>
Enter fullscreen mode Exit fullscreen mode

Now that we have defined the Option type signature we can use it as a discriminated union for our pattern matching.

The pattern-matching package

yarn

yarn add pattern-matching-ts
Enter fullscreen mode Exit fullscreen mode

npm

npm install --save pattern-matching-ts
Enter fullscreen mode Exit fullscreen mode

Now we are ready to implement our option pattern matching.

 * as M from 'pattern-matching-ts/lib/match' 


const optionMatching = M.match<Option<string>, string>({
  Some: (x) => `Some: ${x.value}`,
  None: () => 'Nothing'
})

assert.deepStrictEqual(
  optionMatching(O.some('data')),
   'Some: data'
)
Enter fullscreen mode Exit fullscreen mode

Let's say we have to handle more cases than a simple value that may or not be there...

we can achieve that easily by defining a discriminated union

interface ChangeColor<T = number> {
  readonly _tag: 'ChangeColor'
  readonly value: {
    readonly r: T
    readonly g: T
    readonly b: T
  }
}
interface Move<T = number> {
  readonly _tag: 'Move'
  readonly value: {
    readonly x: T
    readonly y: T 
  }
}

interface Write {
  readonly _tag: 'Write'
  readonly value: {
    readonly text: string
  }
}

type Cases = ChangeColor<number> | Move | Write 
Enter fullscreen mode Exit fullscreen mode

Now we are ready to build our Pattern Matching by implementing all the cases
plus the required default that uses _:=> as a reserved keyword.

import * as M from 'pattern-matching-ts'

const matchMessage = M.match<Cases, string>({
    ChangeColor: ({ value: { r, g, b } }) => `Red: ${r} | Green: ${g} | Blue: ${b}`,
    Move: ({ value: { x, y } }) => `Move in the x direction: ${x} and in the y direction: ${y}`,
    Write: ({ value: { text } }) => `Text message: ${text}`,
    _: () => 'Default message' 
})

const ChangeColor = ({ r, g, b }: ChangeColor<number>['value']) => ({
   _tag: 'ChangeColor', value: { r, g, b }
}) 

assert.deepStrictEqual(
  matchMessage(ChangeColor({ r: 12, g: 20, b: 30 })),
  'Red: 12 | Green: 20 | Blue: 30'
)

assert.deepStrictEqual(matchMessage(null), 'Default message')
Enter fullscreen mode Exit fullscreen mode

pattern-matching-ts source-code

pattern-matching-ts NPM

Top comments (0)

DEV

Thank you.

Β 
Thanks for visiting DEV, we’ve worked really hard to cultivate this great community and would love to have you join us. If you’d like to create an account, you can sign up here.