How the Reflexion Health Tech Team Uses TypeScript Generics to Define Typographic Hierarchies

As one of the first companies to develop virtual rehabilitation technology that works to improve the lives of patients while also helping clinicians better manage their workflows, Reflexion Health is proud to be on the cutting edge of telehealth. 

The (not-so) secret of our success lies largely in the hands of our world-class development team, who have worked to create a highly engaging, easily intuitive interface that’s embraced by patients, clinicians, and experts alike. 

And, as a pioneer in telerehab development, we’re happy to lift the veil on some of the innovative ways in which we’ve met some of the challenges we’ve faced along the way. Today, a member of our development team talks about how we’ve leveraged typescript generics to define typographic hierarchies.

Defining Typographic Hierarchies: The Beyoncé Solution

Reflexion Health’s front-end web development team utilizes TypeScript's static type-checking in our development stack for building web-based applications. In doing so, we encounter our share of challenges. One such challenge: How can we enforce meaningful type relationships between different properties within an object?

To illustrate an appropriate example of hierarchal relationships, let's imagine creating an interface for Beyoncé's hit singles, while also including hit singles from her career as a member of Destiny's Child.

We may define a type for a sample of Beyoncé songs that we use in our application, where we can define possible values for artist, album, and song:

interface beyonceSong {
     artist: 'Beyoncé' | 'Destiny's Child',
     album: 'Survivor' | 'The Writing's on the Wall' | 'Lemonade' | '4',
     song: 'Survivor' | 'Emotion' | 'Say My Name' | 'Jumpin\' Jumpin\'' | 'Lemonade' | 'Sorry' | 'Run the World (Girls)' | 'Love on Top'
}

The issue is figuring out how to ensure that we don't mistakenly return a "beyonceSong" object where 'Run the World(Girls)' has the value "Destiny's Child" under artist, or having "Destiny's Child" as an album by Beyoncé — a tragic modern faux pas. Instead, what we want are type constraints as illustrated in this diagram:

TypeScript Generics: Saving Us from Pop Culture Blunders

With TypeScript generics, we are able to define type constraints between artist, album, and song by passing in generic arguments <T, U, V> for each song object. As we go down the hierarchy of types from artist to album to song, we assign each dynamic property in our object with a generic argument, like so:

interface beyonceSong<T,U,V> {
     artist: T,
     album: U,
     song: V
}

type beyonceCareer = Solo | Group
type Solo<U, V> = beyonceSong<'Beyoncé', U, V>;
type Group<U, V> = beyonceSong<'Destiny's Child', U, V>;

// Types for Beyoncé's hit singles as a solo artist
type Lemonade = Solo<'Lemonade', LemonadeSongs>;
type LemonadeSongs = 'Lemonade' | 'Sorry';

type TheAlbum4 = Solo<'4', TheAlbum4Songs>;
type TheAlbum4Songs = 'Run the World(Girls)' | 'Love on Top';

// Types for Destiny's Child's hit singles
type Survivor = Group<'Survivor', SurvivorSongs>;
type SurvivorSongs = 'Survivor' | 'Emotion';

type TheWritingsOnTheWall = Group<'The Writing's on the Wall', TheWritingsOnTheWallSongs>;
type TheWritingsOnTheWallSongs = 'Say My Name' | 'Jumpin\' Jumpin\'';

Although the result of creating these strictly typed relationships between properties may be expensive in code length, the outcome is precision in data types, which eliminates an entire class of bugs and test cases. 

In the long run, this approach to defining typographic hierarchies achieves greater accuracy and efficiency — a solution that not only reflects our team’s innovative approach to meeting developmental challenges, but also Reflexion’s greater mission to adapt physical therapy to meet the challenges of 21st Century healthcare delivery. 

Katie Huang, B.S. in Computer Science, is a Front-End Software Engineer at Reflexion Health.