Code smells in React with TypeScript Survey
Tags: react, typescript, frontend, webdev
Published: 2023-10-25 16:33:53

👋 Hello! In recent months, my thesis advisor and I have been working on my Bachelor's thesis, aimed at investigating code smells in React applications that use TypeScript. We are looking to invite React developers to participate in a survey to help us identify your perceptions of code smells in React with TypeScript.

Primarily, our research has uncovered code smells in blog posts, forum discussions, and other sources, leading to the identification of a catalog of six code smells, listed below.

## Any Type
TypeScript allows type checking and type inference for variables, objects, functions, etc. By defining the type ``any`` for an element in the code, the developer is disabling type checking, allowing manipulation of these entities without any verification. For example, consider the code below, where a state has its type defined as ``any``. This is problematic because it nullifies the benefits that TypeScript offers in terms of type safety and compile-time error detection. Consequently, without the check, runtime errors may occur.

```tsx
import React, { useState } from 'react';

const MyComponent = () => {
  const [data, setData] = useState<any>(null);

  // ...
};

export default MyComponent;
```

## Many Non-Null Assertions
It is common for developers to use the non-null operator to indicate that a property will not be null or undefined in cases where type checking cannot deduce this. In the following example, the interface ``DataProps`` infers that ``data`` can be of type null. For this reason, the developer uses the non-null operator to assure TypeScript that ``data`` will not be null. Consequently, runtime errors may occur due to a lack of type checking and the possibility of receiving null.

```tsx
function Component({ data }: { data: { prop1: string; prop2: number } | null }) {
  return(
      <div>
        <p>{data!.prop1}</p>
        <p>{data!.prop2}</p>
      </div>
  )
}
```

## Missing Union Type Abstraction
``Type aliases`` in TypeScript are similar to interfaces. However, they accept ``union types``, something interfaces do not allow. Additionally, they facilitate code maintenance, reusability, and code readability. The following example demonstrates a component where the props are of type string, number, or boolean. In this case, it is recommended to use type aliases to avoid the repetition of union types.

```tsx
function Component({ prop }: {
  prop: string | number | boolean;
}) {
  return (
    // ...
  );
}
```

## Enum Implicit Values
Enums are a way to define constants and facilitate code maintenance and readability. However, in TypeScript, it is common for developers to use enums without defining explicit values for them. Therefore, TypeScript assigns numerical values to them following the order. In this example, the constants ``Pending``, ``Processing`` and ``Completed`` are without explicit values. Consequently, if the developer adds a new constant like ``Failed``, it may change the order and the numerical values of the enum.

```tsx
enum PaymentStatus {
  Pending,
  Processing,
  Completed,
}
```

## Multiple Booleans for State
Developers commonly use many boolean-type states to define the component's state. This increases complexity and makes the code difficult to maintain as the number of states and typing increases. In the example shown, the component has some ``useState`` of boolean type, which can be replaced with better typing alternatives.

```tsx
function Component() {
    const [isActive, setIsActive] = useState(false);
    const [isLoading, setIsLoading] = useState(false);
    const [isVisible, setIsVisible] = useState(false);
    // ...
}
```

## Children Props Pitfall
If the component uses children props, developers should opt for the correct typing to be able to pass JSX elements. Therefore, developers should use ``ReactNode`` or some other primitive type, functions, etc. For this reason, it is not a good practice to define ``any`` or types that can restrict JSX elements and affect readability, such as: ``undefined``, ``null``, ``unknown`` and ``never``.

```tsx
type ComponentProps = {
  children: undefined;
};

interface AnotherComponentProps {
  children: never;
}
```

We invite your participation in identifying the frequency and evaluating the potential negative impact of these smells.

If you are interested in contributing to the improvement of React code readability, maintenance, and evolution, please assist us by responding to this survey.

https://forms.gle/nRA26gbEYr8eRXto9

NOTE: Email address collection are disabled. Everything will be anonymous.

Thanks!