At the time when I started my React journey, most developers would have laughed at the idea of using something like TypeScript. For what reason would you take your excellent functional JavaScript and add it with a bunch of type definitions that make it appear as the "Java" programming language. To be honest. I was sceptical of it myself when I first began using it. Now, perhaps it's simply the Stockholm Syndrome talking, yet after using it, I've never wanted to go back from TypeScript. I don't believe I'm separated from everyone else in the world because it was also the second most used technology on the 2021 StackOverflow review.

In this article, you'll learn all you require to begin using TypeScript along with React, and more critically we'll discuss why you should or why you should not be using TypeScript. There's a decent case to be made for both sides of that argument. In case you're new here, subscribe to my blog for more content relating to TypeScript.

JavaScript vs TypeScript code comparison

How about we investigate two unique projects side by side, one with TypeScript and one without. There is a wide range of approaches to add TypeScript to a react application, however in this case I'm utilizing the "create react app" method and specifying the TypeScript format.

npx create-react-app my-app --template typescript
Initialize project

The create-react-app documentation also has instructions for adding it to an existing project. If we take a look at these two projects side by side the clearest difference is that the TypeScript project has files finishing off with ".ts" or ".tsx".

Yet interestingly, If we look inside one of these files, the code is exactly equivalent to the JavaScript files. Following are the codes of a JavaScript and a TypeScript component having the same functionality.

import React from "react";
import logo from "./logo.svg";

export default function Code() {
  return (
    <div className="container">
      <header className="header">
        <img src={logo} alt="logo" />
        <p>This is JavaScript</p>
      </header>
    </div>
  );
}
Javascript.js
import React from "react";
import logo from "./logo.svg";

export default function Code() {
  return (
    <div className="container">
      <header className="header">
        <img src={logo} alt="logo" />
        <p>This is TypeScript</p>
      </header>
    </div>
  );
}
TypeScript.txs

This must raise a decent inquiry "what is TypeScript if my code is exactly similar?". TypeScript as a language is a superset of JavaScript. That implies vanilla JavaScript is moreover substantial TypeScript as such it simply includes extra features on top of ordinary JavaScript. The connection between SCSS and CSS is equivalent to TypeScript and JavaScript, and that is truly awesome because it means you don't need to learn anything new to use TypeScript.

TypeScript Compilers

Here's the catch, normal programs don't have the slightest clue on how to run TypeScript code. That is, we require a compiler to take our TypeScript code and interpret it or compile it down to customary JavaScript.

In our TypeScript project, you'll notice a special file called "tsconfig.json". Its purpose is to customize the behaviour of the compiler which takes your TypeScript code and converts it to any kind of JavaScript that you need to run the program.

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true, 
  }
}
tsconfig.json

Compilers are truly awesome because they allow you to use special features as well as modern JavaScript syntax without stressing whether that code will be upheld on an old legacy program. The drawback is that you currently have this big configuration document to stress over. Ordinarily, everything simply works yet when it doesn't, you need to google this load of various options and sort out what they do. A considerable number of the options are known for how strict TypeScript is with its type checking rules and I'd enthusiastically recommend making it as strict as conceivable by putting strict mode to "true" inside the "tsconfig.json".

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true, πŸ‘ˆ
  }
}
tsconfig.json

This will make your code at first more difficult to write, although over the long haul it makes refactoring and collaborating with different designers way simpler. Another significant choice is "target", which is the version of JavaScript that TypeScript will compile your code to.

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "target": "es5", πŸ‘ˆ
    "strict": true,
  }
}
tsconfig.json

In the code above, the target value "Es5" is the 2009 adaptation of JavaScript, but your codebase is capable of compiling to 2021 JavaScript as well. Sounds amazing right? but you do still be needing TypeScript to do that. In the engine, the JavaScript version of "create-react-app" utilizes a compiler called "babel" that does the same thing. So basically you can compose modern code in both a react "JS" or "TS" project.

Does TypeScript reduce Bugs in code?

Now one thing you'll regularly hear TypeScript engineers say is that it assists them with catching silly bugs in the development before they become enormous debacles in the production. Let me give you an example to check whether that statement is true. Look at this nested invalid code below.

function App() {
  const [data] = useState({});

  return (
    <>
      {data.nested.items}  πŸ‘ˆ
      //Looks good in JavaScript πŸ‘ 
      //But throws an error right away in TypeScript πŸ‘Ž
    </>
  );
}
Deeply nested data with wrong logic

In the JavaScript project, I have some state that is initialized as empty object. Now elsewhere I may reference that object and attempt to access a deeply nested property on it that seems like a legitimate code yet when we run the app, we get an error that it cannot read that property. Bugs like this get dispatched to production constantly. However, if we attempt to do the same thing in TypeScript, it resembles us like a "Prophet" that tells us the error exists even before the application even runs. I truly appreciate that about TypeScript, because I make such silly errors every time.

Although remember that it doesn't replace test-driven development workflow. TypeScript can detect inadequately structured code in advance but can't detect awful logic.

"Type" System

Now, how about we take a glance at the main feature of TypeScript, its "type" system, and the advantages and disadvantages of utilizing it. If we build a functional component in JavaScript (look at the code below), you'll notice that it has a "type" of "JSX.Element". Moreover, if we reference props, they automatically have a "type" value of "any".

const Cool = (props) => {
  //component has a 'type' of "JSX.Element"
  //pros has a 'type' of "any"
  return <> </>;
};
JavaScript prop typeΒ 

Generally speaking, this is an ambiguous "type" definition. In TypeScript, we can be much more explicit about what our code is. React has a built-in type called "FC" which represents "function component" and we can assign that type to the component utilizing a ":" followed by the "type" value. Take a look at this code.

const Cool: FC πŸ‘ˆ= (props) => {
    //component has a type of "FC"
    return <> </>;
  };
  
TypeScript type specification

This tells the compiler the structure of our expected code, which will toss an error for anything that is not a valid prop and autocomplete all the other things as well. By default, the only known react prop is "children" but we can characterize the structure of our props utilizing a TypeScript interface.

Interface

The following code is an example of how an interface is defined.

Interface CoolProps {πŸ‘ˆ
    count: number;
    name: string;
}
Interface

An interface permits you to characterize the structure of an object by giving a property name to each "type". Here, Β we have an interface called "CoolProps" that ought to be an object with two properties - count and name. One of which is a number and the other a string. In its current structure, the interface will make these properties required on the object. However, you can make them optional by adding a question mark after the property name.

Interface CoolProps {
    count?: number; πŸ‘ˆ
    name: string;
}
Interface with optional property

We can now take this interface and pass it to the function component "type" by enclosing it with "< Β >". This resembles saying we have a function component that additionally includes props of its custom shape.

Interface CoolProps {
    count?: number; 
    name: string;
}

const Cool: FC <CoolProps> πŸ‘ˆ= (props) => {
    //component has a type of "FC"
    //props has a type of "CoolProps"
    return <> </>;
  };
Functional component with an interface

Now, we get truly helpful IntelliSense in Visual Studio Code on any props when working with this component and it additionally ensures that we use the correct props when working with this component in JSX.

Anyway, that came at a significant cost. We've added a great deal of boilerplate to the code base without adding any new functionality for the user. Now it is feasible to bypass type checking at whatever point you need by essentially adding the "any" type that permits you to quit TypeScript but in addition contradicts the entirety of its advantages.

const Cool: FC <any> πŸ‘ˆ= (props) => {
    //pros has a type of "any"
    return <> </>;
  };
Using "any" type in TypeScript

It's additionally important to know that TypeScript can give automatic documentation to your code because your editor will automatically pick up the type definitions. So anyone utilizing your code will get IntelliSense on the shape and reason concerning it. And that is way far more efficient than going to read some documentation online. Now, in many scenarios TypeScript can automatically gather the type without you adding any extra code to the code base. For instance, you could firmly write the useState hooks "type" like given below.

const Cool: FC<CoolProps> = (props) => {
    const [data, setData] = useState <string>πŸ‘ˆ("hello world")
  };
Strong type definition everywhere

However using "< Β >" may not be necessary here because, if you have a default value of a string, you don't have to give it an explicit type. it'll have a "string" type automatically. On the off chance that you attempt to change it to a value that is not a string, you'll get that prophetic error in VS Code. So that is quite useful.

Conclusion

So, is TypeScript truly awesome?

It is supervised by Microsoft, enterprises love it, debugging is easier, but anyway, some guy on the internet also said it only catches 15% of bugs and Airbnb said it catches 38% of bugs and so on. In the end, it comes down to - Would you rather write more code or would you rather be more confident in the code you do write now? Would you rather take a look at your documentation on an online site or would you rather have it be automatic in your editor? Would you rather write quicker code now or manage chaotic refactoring later? Do your friends love it or do they despise it? Β These are all trade-offs that you need to think about. However, as I would see it, the response to should you use TypeScript with react is clearly ..........