JavaScript is a weakly-typed programming language. Because of this, it's very lenient, and programming errors are likely to go unnoticed during development. TypeScript, a JavaScript type-checking library, solves this problem by enforcing types on values. This article will teach you how to create a React project with TypeScript.

Creating a React Project With TypeScript

The create-react-app command allows you to create Typescript projects using the --template option.

To create a new React project with TypeScript, run the following command:

        npx create-react-app app-name --template typescript

This command creates a new React and TypeScript project from scratch. You can also add TypeScript to an existing React application.

To do so, navigate to the project to which you want to add TypeScript and run the following code:

        npm install --save typescript @types/node @types/react @types/react-dom @types/jest

Then swap the .js file extension with .tsx for files you want to convert to TypeScript. Once you do this, you will get the “React refers to a UMD global, but the current file is a module.” error. You can solve it by importing React in every typescript file like this:

        import React from "react"

As a more straightforward solution, create a tsconfig.json and set jsx to react-jsx.

        {
    "compilerOptions": {
      "jsx": "react-jsx",
      "target": "es6",
      "module": "esnext",
    },
}

You can find all the configuration settings from the TypeScript docs.

Creating a React Function Component in TypeScript

You define a React function component in the same way as a JavaScript function.

Below is an example of a function component called Greetings.

        export default function Greetings() {
  return (
    <div>Hello world</div>
  )
}

This component returns a “Hello world” string, and TypeScript infers its return type. However, you can annotate its return type:

        export default function Greetings(): JSX.Element {
  return (
    <div>Hello world</div>
  )
}

TypeScript will throw an error if the Greetings component returns a value that is not a JSX.element.

Using React Props With TypeScript

React allows you to create reusable components through props. For instance, the Greetings component can receive a name prop such that the return value is customized based on it.

Below is the edited component with a name as a prop. Notice the inline type declaration.

        function Greetings({name}: {name: string}) {
  return (
    <div>Hello {name}</div>
  )
}

If you pass the name “Jane”, the component will return the message “Hello Jane”.

Instead of writing the type declaration in the function, you can define it externally like this:

        type GreetingsProps = {
  name: string;
};

Then pass the defined type to the component as follows:

        function Greetings({name}: GreetingsProps) {
  return (
    <div>Hello {name}</div>
  )
}

Use the interface keyword if you are exporting this type and want to extend it:

        export interface GreetingsProps {
  name: string;
};

Note the syntax difference between type and interface — interface does not have an equal sign.

You can extend an interface using the following code:

        import { GreetingsProps } from './Greetings'

interface WelcomeProps extends GreetingsProps {
    time: "string"
}

You can then use the extended interface in another component.

        function Welcome({name, time}: WelcomeProps) {
  return (
    <div>
Good {time}, {name}!
    </div>
  )
}

Use the “?” symbol with your props interface to define optional props. Here is an example of an interface with an optional name prop.

        interface GreetingsProps {
  name?: string;
};

If you don't pass a name prop, TypeScript will not throw any error.

Using React State With TypeScript

In plain JavaScript, you define the useState() hook as follows:

        const [customerName, setCustomerName] = useState("");

In this example, TypeScript can easily infer the type of the firstName as a string because the default value is a string.

However, sometimes you can't initialize the state to a defined value. In these cases, you must give a state value type.

Here are some examples of how to define types in the useState() hook.

        const [customerName, setCustomerName] = useState<string>("");
const [age, setAge] = useState<number>(0);
const [isSubscribed, setIsSubscribed] = useState<boolean>(false);

You can also use an interface in the useState() hook. For example, you can rewrite the above example to use an interface shown below.

        interface ICustomer {
    customerName: string ;
    age: number ;
    isSubscribed: boolean ;
}

Use the custom interface in the hook like this:

        const [customer, setCustomer] =  useState<ICustomer>({
        customerName: "Jane",
        age: 10,
        isSubscribed: false
      });

Using Events With TypeScript

Events are essential as they allow users to interact with a webpage. In TypeScript, you can either type events or the event handlers.

To demonstrate, consider the following Login component using the onClick() and onChange() events.

        import { useState } from 'react';

export default function Login() {
  const [email, setEmail] = useState('');
 
  const handleChange = (event) => {
    setEmail(event.target.value);
  };
 
  const handleClick = (event) => {
    console.log('Submitted!');
  };
 
  return (
    <div>
      <input type="email" value={email} onChange={handleChange} />
      <button onClick={() => handleClick}>Submit</button>
    </div>
  );
}

This is how you would handle events in plain JavaScript. TypeScript, however, expects you to define the event parameter type in the event handler functions. Luckily, React provides several event types.

For example, use the changeEvent type for the handleChange() event handler.

        import { ChangeEvent, useState } from 'react';

const handleChange = (event:ChangeEvent<HTMLInputElement>) => {
    setEmail(event.target.value);
};

The changeEvent type is used for changing the values of input, select, and textarea elements. It is a generic type meaning you must pass in the DOM element whose value changes. In this example, you passed the input element.

The above example demonstrates how to type the event. The code below shows how you can type the event handler instead.

        import { ChangeEventHandler, useState } from 'react';
 
const handleChange: ChangeEventHandler<HTMLInputElement> = (event) => {
    setEmail(event.target.value);
};

For the handleClick() event, use the MouseEvent().

        import { useState, MouseEvent } from 'react';

const handleClick = (event: MouseEvent) => {
    console.log('Submitted!');
};

Again, you can attach the type to the event handler itself.

        import { useState, MouseEventHandler } from 'react';

const handleClick: MouseEventHandler = (event) => {
    console.log('Submitted!');
  };

For other event types, check out the React TypeScript cheat sheet.

If you are creating large forms, it's better to use a form library like Formik, as it's built with TypeScript.

Why Should You Use TypeScript?

You can instruct a new React project to use TypeScript or convert an existing one. You can also use TypeScript with React function components, state, and React events.

Typing React components can sometimes feel like writing unnecessary boilerplate code. However, the more you use it, the more you'll appreciate its ability to catch errors before you deploy your code.