Lose coupling abstractions in React using TypeScript
Tags: typescript, javascript, react, programming
Published: 2021-01-13 12:14:16

---
title: Lose coupling abstractions in React using TypeScript
description: Building React components with OOP design principles in mind can really take a turn in how the component will behave in the future and how easy it will be to be used. This article is a simple introduction of the concept for Liskov Substitution Principle and how React components and the benefits of applying it in React.
tags:
  - typescript
  - javascript
  - react
  - programming
published: true
---
## Overview
Building React components with OOP design principles in mind can really take a turn in how the component will behave in the future and how easy it will be to be used. This article is an introduction of the concept for Liskov Substitution Principle and how React components and the benefits of applying it in React.

## General Idea

The idea behind the principle is that objects of a superclass should be replaceable with objects of its subclasses without breaking the application. This requires the superclass object to behave the same way as the subclass and to have the same input. 

In React terms, if we remove an abstraction of a component, then the component should behave the same way as it was while using the abstraction.

## Enforcing the Liskov Substitution Principle in React

Let's see this in action.
We need to build a custom React component library. One of the components we will need to use is a custom Button. The Button component will need to have the same functionality as the usual button except for the style of the button, which will be closed for modification.

The props interface for the button will look like this:

```
interface IButtonProps extends Omit<React.HTMLAttributes<HTMLButtonElement>, "style"> {}
```

Let's examine the interface.

- `IButtonProps` extends the HTML attributes of the native HTML `<button/>`, e.g. `React.HTMLAttributes<HTMLButtonElement>`. This way we can just reuse the attributes from the native `<button/>` instead of writing them manually.

The beauty of this approach is that if we decide to ditch the custom Button component and just use the default `<button/>`, it will just work.

Another **BIG** plus for using this approach is that the rest of the team will already be familiar with the custom Button's interface as the props are inherited by the native HTML element.

- The next thing to look at is the word `Omit`, used when declaring the interface. `Omit` is a Typescript helper which helps to unselect properties from a provided interface. Omitting multiple props can be done by using the `|` operator like this:

```
interface IButtonProps extends Omit<React.HTMLAttributes<HTMLButtonElement>, "style" | "className"> {}
```

Now, let's declare the custom Button component

```
const style = {
  // My custom Button style
};

function Button(props: IButtonProps) {
  return <button {...props} style={style} />;
}
```

Another thing that needs mentioning here is how the props are passed to the `<button/>`. To make sure that the `style` prop cannot be overridden by the `props`, by any chance, we should define the `style` prop after destructuring the rest of the `props`. This way even if `style` prop has been passed via the properties, it will be overridden by our custom styling. Even if someone decides to ignore the TypeScript error, this will still prevent them from passing that `style`.  

This all looks great so far, but let's see another example. <Br/> As part of the component library, we need to build a custom `Paragraph` component. We need to make sure that we can apply some of the styling, e.g. `text-align`, `font-weight`... Keep in mind the idea again is to enforce the Liskov Substitution Principle.

For this example we can build our interface as shown below:

```
interface IParagraphProps extends React.HTMLAttributes<HTMLParagraphElement> {
  style?: Pick<
    React.CSSProperties,
    "textAlign" | "fontWeight"
  >;
}
```

Let's dig in and see what is happening.

The `IParagraphProps` extends the native HTML `<p/>` element's attributes. Like the custom Button, the idea is to share the same properties as the native element. The next thing defined is the `style` property. The word `Pick` is another TypeScript helper which allows to _pick_ some of the properties from a predefined interface. In this case, the component will only allow for `textAlign` and `fontWeight`.

Let's implement the Paragraph component.

```
const style = {
  // My custom Paragraph style
};

function Paragraph(props: IParagraphProps) {
  return <p {...props} style={{ ...style, ...props.style }} />;
}
```

## Conclusion
We just saw how the Liskov Substitution Principle can be enforced when building React components using TypeScript. This allows us to reuse the attributes of the native elements on the abstraction and to pick only the functionality the custom components are allowed to implement without breaking the interface between the abstraction and the native element.