Rafał Budzis
Frontend Developer
Robert Awdziej
Technical Team Lead
Małgorzata Maskiewicz
Reviewed by a tech expert

Generics and illegal states in TypeScript

Read this articles in:

At the last meeting of our frontend dev community, Rafał talked about a piece of code he wrote in RST over four years ago. The code stood the test of time – it is well-thought-out and, therefore, worth revisiting. Moreover, it was an excellent opportunity to use it as an example of working with generics in TypeScript.

The code analyzed during the meeting was responsible for downloading data. Not a big deal, right? Well, the whole application is based on list and detail views. This is the most important part of the application.

Generics in TypeScript

The system is based mainly on react-redux and RxJS and used to handle async data fetch in store. The client required two things: fast response time, which we achieved through cache, and retries for requests that end in error.

Generics allowed us to make one system that handles all lists and tables in exactly the same way! There was no cache code duplication or repetition of requests.

How does the system work? A React component uses a hook responsible for fetching data from the store or sending an action to the store to fetch the data from the backend. The sent event goes to RxJS, which communicates with the service performing the appropriate query by selecting a service dedicated to a specific list. The data goes back to RxJS, and then, based on the data, an event is created for the reducer.

Nowadays, it is worth considering using a react-query for this type of function. But four years ago, there were no such solutions. And that's good because we could use the code to discuss how generics work in TypeScript!

Illegal states in TypeScript

The second part of the meeting was dedicated to illegal states in TypeScript, and then, consequently, the next topic was creating a business domain model. The types that TypeScript offers allow for a good mapping of business requirements in the project code. And thanks to understanding the topic of illegal states, our code will be cleaner and less prone to errors.

Illegal states can be easily visualized with an interface that models the functionality in such a flexible way that it allows the creation of objects that meet this interface in a way that is difficult to explain in the context of the application.

The discussed subject, without going into details, can be based on a simple example. Let's take a state contract for loading service responses like in the example below:

interface LoadingState {
  isLoading: boolean;
  responseData?: ResponseData;
  error?: Error;

In an extreme case, we can create an object that meets the above interface:

const loadingState = {
  isLoading: true,
  responseData: { data: {/* some data */} },
  error: new Error(‘I have no idea what happened here.’),

It's hard to tell the loading state from the contents of this object in right now, isn't it?

This can be solved with a little refactoring and union of objects defining the allowed states:

type LoadingState =
  { status: LoadingStatus.Loading }
  { status: LoadingStatus.Error, error: Error }
  { status: LoadingState.Success, responseData: ResponseData };

In the context of business domain modeling, we discussed an example of a user interface that, in a given use context, can be of two types, distinguished by a specific group of fields.

Based on the above example, we can imagine how to model an interface that meets business rules, and allows for the creation of illegal states. In such cases, it is helpful to separate parts of the overlapping fields from both business cases to the general interface, and prepare dedicated types for business cases to extend this interface. The union of prepared types defines the final interface that satisfies the business rules and is safe in terms of illegal states.


People also ask

No items found.
Want more posts from the author?
Read more
Read more

Want to read more?

RST Lifestyle

New employee onboarding process – effective implementation in any work model

Are you thinking about joining our team? Here's what you can expect on your first days of work.
RST Lifestyle

RST Software x Meet.js Wrocław

Here's a wrap of our recent collaboration with meet.js in Wrocław! Kacper Szewczyk spoke about optimizing frontend applications - we've included a link to his talk.
RST Lifestyle

Advanced typing in TypeScript with generics

There are three main generic function types: conditional types, mapped types, and template literal types. Learn what they are and how to use them through our examples.
No results found.
There are no results with this criteria. Try changing your search.