👋 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;
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 }) {
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.
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.
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.
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
.
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!
Top comments (2)
For new devs using typescript + react: this is the most comprehensive and complete reference, docs and tips I found and use
github.com/typescript-cheatsheets/...
more readable web version - react-typescript-cheatsheet.netlif...
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)