DEV Community

Cover image for Top 5 mistakes to avoid when using React with Typescript
Bhaveek Jain
Bhaveek Jain

Posted on

Top 5 mistakes to avoid when using React with Typescript

Introduction

Are you looking for a comprehensive introduction to using React with TypeScript? Look no further! In this blog, we'll delve into the top 5 mistakes to avoid when combining these powerful tools. As a JavaScript library, React allows developers to build reusable UI components and create efficient, dynamic user interfaces. When paired with TypeScript, a typed superset of JavaScript, developers can take advantage of enhanced type checking and improved code reliability. However, it's important to be mindful of potential pitfalls and mistakes that can arise during the development process. By following best practices and avoiding common errors, you can ensure a smooth and successful experience when using React with TypeScript. In this blog, we'll follow practical tips and helpful examples to guide you on your journey.

1. Using Type "any"

Typescript allows you to assign types to variables. Usually, it's done in this way

let someNumber: number;
Enter fullscreen mode Exit fullscreen mode

lets do some operations

let someNumber: number;
someNumber = 10; // โœ… All good
someNumber = "ten"; // โŒ Error
// Type 'string' is not assignable to type 'number'/. (2322) 
Enter fullscreen mode Exit fullscreen mode

Here when we assign our variable a number, it worked fine but gave an error when provided it with a string.

We can roughly describe types as a collection of possible things that are similar in some way. For example, type number contains numbers from 0.5,2,3 ... 1000 ... 122123 etc. You get the point that they are somewhat similar and have a specific behaviour like addition, subtraction etc. But there are also some operations that are not available for certain types, say for number in our case, we can't really get a member of a number or 2[3]

So if we write this in typescript

const someNumber: number = 10
const tuff = someNumber[3]
Enter fullscreen mode Exit fullscreen mode

we'll receive this error

any-error

Let's modify our example a bit and see what impact it makes

const someNumber: any = 10
const tuff = someNumber[3]
Enter fullscreen mode Exit fullscreen mode

any-implementation
Suddenly, the errors are gone!

Welcome to "any" land!
The biggest problem with any is that it's positive/permissive. It's similar to something if I say: "I drink something." You would assume I'm drinking something drinkable like lemonade or water. You wouldn't expect me drinking acid or petrol, in which case I'd need immediate medical attention(lol)

Using the any type can make your code less reliable and more prone to runtime errors, because the compiler is not able to catch type-related issues. It can also make your code less readable and maintainable, because it's not clear to other developers what the type of a value is.

Instead of using the any type, it's generally best to use explicit type annotations or type declarations to specify the types of values in your code. This will help ensure that your code is correctly typed and can be more easily understood by others.

2. Using Type Assertions

Type assertions, also known as type casting, are a way to override the type of a value in TypeScript. While they can be useful in certain situations, it's generally recommended to avoid using type assertions in your code. This is because type assertions can mask type-related issues and hinder the effectiveness of the TypeScript compiler.

For example, if you use a type assertion to tell the compiler that a value has a certain type, the compiler will trust your assertion and will not perform any further type checking on that value. This means that if there is a type mismatch or error in your code, it may not be caught by the compiler and could lead to runtime errors.

Additionally, type assertions can make your code less readable and maintainable. They can obscure the actual type of a value and make it more difficult for other developers to understand your code.

In general, it's best to let the TypeScript compiler do its job and catch type-related issues as early as possible in the development process. Instead of using type assertions, you can use type annotations or type declarations to explicitly specify the types of variables and values in your code. This will help ensure that your code is correctly typed and can be more easily understood by others.

For example,

let someValue: any = 'this is a string';

let strLength: number = (someValue as string).length;
Enter fullscreen mode Exit fullscreen mode

we have a variable someValue with the type any, which means it can hold any value. We then use a type assertion to tell the compiler that someValue is a string, and we access the length property of the string.

However, as mentioned earlier, it's generally recommended to avoid using type assertions like this. Instead, we could have explicitly declared the type of someValue as a string, like this:

let someValue: string = 'this is a string';

let strLength: number = someValue.length;
Enter fullscreen mode Exit fullscreen mode

This approach is more type-safe and makes it clearer to other developers what the type of someValue is. It also allows the TypeScript compiler to catch any type-related issues that may arise.

3. Using @ts-ignore

The @ts-ignore directive is a way to tell the TypeScript compiler to ignore a specific piece of code. It can be used to suppress type-related errors or warnings that the compiler generates.

We declare @ts-node as a comment on the line we want to ignore to type check

if(false) {
// @ts-ignore // Unreachable code block
  console.log("Hello ts-ignore")
}
Enter fullscreen mode Exit fullscreen mode

It's a good practise to provide a reason for why we are ignoring this.

After we discussed how important it is to not use type any and not use type assertions too much, it might be surprising that such option even exist.
It can be useful:

  • In tests, when you want to verify that invalid inputs will be handled correctly
  • When you are migrating to newer version of typescript
  • When you are migrating javascript code base to typescript

While @ts-ignore can be useful in certain situations, it's generally not recommended to use it excessively in your code. This is because @ts-ignore can mask type-related issues and hinder the effectiveness of the TypeScript compiler.

For example, if you use @ts-ignore to suppress a type error, the compiler will not report the error and the issue will not be addressed. This can lead to runtime errors or unexpected behavior in your code.

Additionally, using @ts-ignore can make your code less maintainable and harder to understand, because it's not clear why the compiler is being told to ignore a certain piece of code.

It's generally best to let the TypeScript compiler do its job and catch type-related issues as early as possible in the development process. Instead of using @ts-ignore, you can try to fix the underlying issue or use explicit type annotations or type declarations to specify the types of values in your code. This will help ensure that your code is correctly typed and can be more easily understood by others.

4. Using numeric enums

An enum (short for "enumeration") is a way to define a set of named constants. Enums can be either numeric or string-based.

Numeric enums are enums in which the values are numeric. In a numeric enum, the values are automatically assigned incrementing numbers starting from 0.

Here is an example of a numeric enum in TypeScript:

enum Days {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}
In this example, the enum `Days` has seven values: `Monday`, `Tuesday`, `Wednesday`, `Thursday`, `Friday`, `Saturday`, and `Sunday`. These values are automatically assigned the numbers 0 through 6, respectively.
Enter fullscreen mode Exit fullscreen mode

While numeric enums can be useful in certain situations, it's generally recommended to avoid using them in your TypeScript code. This is because numeric enums can be prone to errors and can make your code less readable and maintainable.

One issue with numeric enums is that they can lead to runtime errors if you are not careful. For example, consider the following code:

enum Days {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

let day = Days.Monday;

if (day === Days.Sunday) {
  console.log('Today is Sunday');
} else {
  console.log('Today is not Sunday');
}
Enter fullscreen mode Exit fullscreen mode

In this example, we have an enum Days with seven values representing the days of the week. We then assign the value Days.Monday to the variable day.

However, because we are using a numeric enum, the values of the enum are automatically assigned incrementing numbers starting from 0. This means that the value of Days.Monday is actually 0, and the value of Days.Sunday is 6.

enumNumeric

As a result, the condition in the if statement will evaluate to false, and the message "Today is not Sunday" will be logged to the console. This can be confusing and lead to unexpected behavior in your code.

Another issue with numeric enums is that they can make your code less readable and maintainable. It's often not immediately clear what a numeric value represents in an enum, and it can be easy to mix up the values or use them incorrectly.

For these reasons, it's generally recommended to use string-based enums instead of numeric enums in your TypeScript code. String-based enums provide better type safety and are easier to understand, which can make your code more reliable and maintainable.

5. Marking fields optional

In TypeScript, you can use the ? symbol to mark a field or parameter as optional. This means that the field or parameter is not required and does not need to be provided when the code is called.

While marking fields or parameters as optional can be useful in certain situations, it's generally not recommended to do so excessively. This is because optional fields and parameters can make your code less reliable and more prone to runtime errors.

For example, consider the following code:

function greet(name?: string) {
  console.log(`Hello, ${name}!`);
}

greet();
Enter fullscreen mode Exit fullscreen mode

In this example, the name parameter of the greet function is marked as optional. This means that the function can be called without providing a value for name.

However, if the function is called without a value for name, the console.log statement will result in an error, because the name variable will be undefined. This can lead to unexpected behavior and runtime errors in your code.

optional
To avoid this issue, it's generally best to make fields and parameters required by default, and only mark them as optional if there is a specific need to do so. This will help ensure that your code is more reliable and less prone to runtime errors.



Thank you for reading this blog. I hope that you have found this information helpful and that it will help you avoid common pitfalls and mistakes when working with these powerful tools.

Using React with TypeScript can greatly improve the reliability and maintainability of your code, but it's important to be aware of potential issues and mistakes that can arise. By following best practices and avoiding these common errors, you can ensure a smooth and successful development process.

If you have any further questions, comment it out and I'll try to answer in best possible way. Thanks for reading!

Top comments (0)