Going Deeper: Production ReactExtra· 45 min read

React with TypeScript

Add types to your components, props, state and events — the way nearly every modern React job writes code.

What you will learn

  • Type a component’s props with an interface
  • Type useState and event handlers
  • Read the errors TypeScript gives you

Why type your React?

You met TypeScript in its own module — JavaScript with types, little labels that say what kind of value something is (a number, a string, an object of a certain shape). React projects use TypeScript to catch mistakes before the app runs: passing the wrong prop, reading a field that does not exist, or forgetting a required value. In a real React job, the files are usually .tsx (TypeScript + JSX) rather than .jsx.

The good news: it is the same React you already know. You just add type labels in a few places — to props, to state, and to event handlers. Let us see each one.

Typing props with an interface

An interface is a named description of an object’s shape — which fields it has and what type each one is. We describe what props a component expects, then attach that interface to the component. This snippet runs in a real Vite + TypeScript project, so it is shown as code with its Output.

Greeting.tsx — props described by an interface
// Greeting.tsx
interface GreetingProps {
  name: string;
  age: number;
  isStudent?: boolean;   // the ? means optional
}

function Greeting({ name, age, isStudent }: GreetingProps) {
  return (
    <div>
      <h3>Hello, {name}!</h3>
      <p>Age: {age}{isStudent ? ' (student)' : ''}</p>
    </div>
  );
}

// Using it:
// <Greeting name="Asha" age={20} isStudent />        ok
// <Greeting name="Ravi" />                            ERROR: 'age' is missing

Reading it piece by piece:

  1. The interface GreetingProps block lists the three props and their types: name is a string, age is a number, and isStudent is an optional boolean (the ? after its name means “you may leave this out”).
  2. We attach the interface to the component’s parameter: function Greeting({ name, age, isStudent }: GreetingProps). The : GreetingProps part says “these props must match that shape”.
  3. Now the editor helps you. <Greeting name="Asha" age={20} isStudent /> is fine. But <Greeting name="Ravi" /> shows a red error before you ever run the app, because the required age prop is missing.

Note: Output (the error TypeScript shows for the bad usage): Property 'age' is missing in type '{ name: string; }' but required in type 'GreetingProps'. The mistake is caught while you type — not by a confused user later.

Typing useState

Most of the time TypeScript can figure out the type of state by itself. When it cannot (an empty list, or a value that might be missing), you tell it with angle brackets useState<Type>(...).

Telling useState what type of data it holds
const [count, setCount] = useState(0);            // inferred as number
const [name, setName] = useState('');             // inferred as string

// When the type is not obvious, write it yourself:
interface User { id: number; name: string; }
const [users, setUsers] = useState<User[]>([]);   // a list of User, starts empty
const [user, setUser] = useState<User | null>(null);  // a User, or nothing yet

Walking through it: for useState(0) TypeScript sees the 0 and decides count is a number — no extra work needed. But useState([]) is an empty array; TypeScript cannot guess what goes in it, so we write useState<User[]>([]) to say “a list of User objects, currently empty”. The User | null form (a union type, meaning “one of these”) is the standard way to say “we do not have the user yet, but we will”.

Typing event handlers

When you handle an event like typing in an input, the event object e has a type. TypeScript ships the right type for each event so e.target.value autocompletes correctly.

A typed onChange handler
function SearchBox() {
  const [text, setText] = useState('');

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setText(e.target.value);   // .value is known to be a string
  }

  return <input value={text} onChange={handleChange} placeholder="Search" />;
}

The label e: React.ChangeEvent<HTMLInputElement> tells TypeScript “this is a change event coming from an <input>”. Because of that, e.target.value is known to be a string and the editor autocompletes it. If you instead read e.target.valeu (a typo) or expected a number, TypeScript would flag it immediately. You rarely memorise these event types — you start typing React. and the editor suggests the right one.

Note: Common event types worth recognising: • React.ChangeEvent<HTMLInputElement> — typing in a text input • React.FormEvent<HTMLFormElement> — submitting a form • React.MouseEvent<HTMLButtonElement> — clicking a button

Tip: Start a project with TypeScript from day one: npm create vite@latest my-app -- --template react-ts. The -ts template gives you .tsx files and type-checking out of the box.

Watch out: Do not reach for the any type to “make the error go away”. any switches off all checking for that value and hides the very bugs TypeScript is meant to catch. Prefer a proper interface or a union type.

Q. How do you describe the shape of a component’s props in TypeScript?

Answer: An interface (or type alias) lists each prop and its type; you attach it with `{ ... }: PropsType`, and TypeScript checks every usage.

✍️ Practice

  1. Write a ProductCardProps interface (name: string, price: number, inStock?: boolean) and type a ProductCard component with it.
  2. Type a useState that holds a list of objects, starting empty.

🏠 Homework

  1. Convert one earlier .jsx component to .tsx: add a props interface, type its state, and type one event handler.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →