DEV Community


Posted on

Code smells in React with TypeScript Survey

👋 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.

import React, { useState } from 'react';

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

  // ...

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

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.

function Component({ data }: { data: { prop1: string; prop2: number } | null }) {
Enter fullscreen mode Exit fullscreen mode

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.

function Component({ prop }: {
  prop: string | number | boolean;
}) {
  return (
    // ...
Enter fullscreen mode Exit fullscreen mode

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.

enum PaymentStatus {
Enter fullscreen mode Exit fullscreen mode

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.

function Component() {
    const [isActive, setIsActive] = useState(false);
    const [isLoading, setIsLoading] = useState(false);
    const [isVisible, setIsVisible] = useState(false);
    // ...
Enter fullscreen mode Exit fullscreen mode

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.

type ComponentProps = {
  children: undefined;

interface AnotherComponentProps {
  children: never;
Enter fullscreen mode Exit fullscreen mode

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.

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


Top comments (3)

adaptive-shield-matrix profile image
Adaptive Shield Matrix

For new devs using typescript + react: this is the most comprehensive and complete reference, docs and tips I found and use
more readable web version - react-typescript-cheatsheet.netlif...

  • Its a big list, so take your time going it through
  • Fully discussed in the open with a very big community
xr0master profile image
Sergey Khomushin • Edited

For "Children Props", it's better to use the "PropsWithChildren" type from React.
The "Multiple Booleans for State" is also not obvious; it can be in loading and visible state in the same time, for example.

In fact, this survey does not apply to React, but to TS. Instead of React, we can use NodeJS (as an example) and everything will be the same. (almost)