Frontend Dad Blog.

Typescript and React: The Basics

Cover Image for Typescript and React: The Basics

Typescript pairs quite nicely with React, though it can be a bit hard to jump into. Typescript won't solve problematic React design patterns, of which there are plenty, but it will make React apps safer. And you know, now that I think of it, maybe it will help you avoid some shitty React design patterns...

Oh yeah, just read this.

Setup

Depending on your setup, you'll have to do some extra configuration to get Typescript running in a React project. Your best bet is to check the official Typescript docs here. Ideally, you won't have to do much beyond adding a few types and potentially tweaking any webpack/bundling configuration you've got set up. Godspeed!

TSX: It's like JSX, only typed (duh)

Alright. Assuming you've got Typescript configured properly, let's check out a very simple typed functional component.


function FooComponent(): JSX.Element {
  return <h1> foo </h1>;
}

We've typed the return value of the FooComponent. This is not strictly necessary, and will really only serve if you were to somehow return undefined from the component without intending to. Still, it's illustrative. We want FooComponent to ultimately return a JSX element, so we're simply declaring that. I will omit the return type in subsequent examples.

No more Prop Types!

Typescript really shines in React when it comes to being declaritive and strict about the APIs for our components. In React, we determine the API of a component through its props. Before Typescript, developers used the Prop-Types package alongside linting setups to attempt guard against bad props being passed to components. However, this pattern is brittle and still could result in bad types being passed in at run time.

Enter Typescript.

Typescript allows us to simply declare our props as Types (or interfaces, more on that shortly). If we properly leverage these types, Typescript will warn us about bad types during build time (or even earlier, depending on your editor).

We declare a type for our props, and tell Typescript:

interface FooProps = {
  name: string;
}
function FooComponent({name}: FooProps): JSX.Element {
  return <h1> foo </h1>;
}

It's more common now to see interfaces used instead of types as it applies to props. Interfaces and Types are so similar that the official Typescript documentation describes them as effectively interchangeable. Why are interfaces preferred? Probably because interfaces are techinically a bit simpler - they can't be extended and "unionized" in the same way types can. The differences are subtle and you can read more about them here.

Regardless, if someone tries to get into a philosophical or technical debate with you (or god forbid asks an interview question) about interfaces vs. types as it relates to React, then watch out, because this person is a shithead.

Functions and handlers

We can type the inputs/outputs of our functions passed as props. It's very rare for a function to be passed to a component as a prop that has a return type. This is one of the things that had me thinking, "Hey, maybe Typescript can help us avoid shitty React design patterns." Anyway, your functions are going to come down, and they will almost certainly consume synthetic events. More on that in the next section. Check out the below example:

interface FooProps = {
  name: string;
  doSomething: (a: string) => void;
}
function FooComponent({name, doSomething}: FooProps): JSX.Element {

  useEffect(()=> {
    doSomething()
  }, [])

  return <h1> {name} </h1>;
      
}

We've told Typescript that any props passed to this component as doSomething better follow that signature, or else! And as mentioned above, if your function-as-a-prop actually returns something, you've probably fucked up somewhere. void or undefined are common ways to express a function that does not return any value.

DefaultProps

Nope

Synthetic Events / Handlers

It's quite common to pass event handling functions around as props in React. With Typescript and props-as-interfaces, we can be very descriptive of not only what kind of event a given function handles, but also the target of the event...

interface FooProps = {
  name: string;
  handleClick: (event: React.MouseEvent<HTMLButtonElement>): void;
}
function FooComponent({name, handleClick}: FooProps): JSX.Element {
  return (
    <>  
      <h1> {name} </h1>
      <button onClick={handleClick}> clicky! </button>
    <>
  )
}

Above, we are letting Typescript know that handleClick takes a mouse event, here expressed as typed React synthetic event as its argument. We then tell Typescript that the target of the event is a button element. This is done using Typescript generics, which are a pretty tricky concept and worthy of their own blog post. Blog post alert!

TLDR;

There are many more opportunities to take advantage of Typescript in a React application (especially when it comes to helper functions that munge data). Props are the basics - say goodbye to Prop-Types! Weeeee!