codewithjohn.dev
Published on

How to Use TypeScript with React Components

In this article, we will explore how to effectively use TypeScript with React components.

TypeScript is a popular way to add type definitions to JavaScript code. When working with React components, TypeScript can greatly enhance code quality and helps catch errors at compile-time. In this post i am going to focus on props type validation, handling optional props, using the children prop, and enforcing return types.

Table of Contents

Props type validation with TypeScript

Props type validation means specifying the expected types of the properties that a component expects.

To add type validation for your props with TypeScript first define the type or interface that describes what props the component should accepts.

Next, in your component, you can specify the props type using the React.FC (FunctionComponent) type. Pass in the props type as a generic argument to indicate the expected prop types.

For example, let's say we are building a component that displays list of posts.

First, let's create a Post interface to define the structure of a blog post:

interface Post {
  id: number
  title: string
  content: string
  author: string
  date: string
}

Next, we'll create a PostList component that displays a list of blog posts:

import React from 'react'

interface PostListProps {
  posts: Post[]
}

const PostList: React.FC<PostListProps> = ({ posts }) => {
  return (
    <div>
      <h1>Blog Posts</h1>
      {posts.map((post) => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
          <p>Author: {post.author}</p>
          <p>Date: {post.date}</p>
        </div>
      ))}
    </div>
  )
}

export default PostList

As we can see from the above example in the PostListProps interface, the prop for the PostList component can only be an instance of Post. In the PostList component, we are trying to access post.title post.content post.author post.date so If we don't pass an instance of Post in the PostList component, TypeScript will give a compile-time error.

Now, we can use the PostList component in our main App component by passing list of posts.

import React from 'react'
import PostList from './PostList'

const App: React.FC = () => {
  const posts: Post[] = [
    {
      id: 1,
      title: 'My First Blog Post',
      content: 'Lorem ipsum dolor sit amet...',
      author: 'John Doe',
      date: '2022-01-01',
    },
    {
      id: 2,
      title: 'React Tips and Tricks',
      content: 'Lorem ipsum dolor sit amet...',
      author: 'Jane Smith',
      date: '2022-01-05',
    },
  ]

  return (
    <div>
      <h1>My Blog</h1>
      <PostList posts={posts} />
    </div>
  )
}

export default App

Optional Props with TypeScript

Sometimes, we might want to make certain props optional. We can achieve this by adding a ? after the prop name in the interface definition. Here's an example

import React from 'react'

type ButtonProps = {
  text: string
  onClick: () => void
  color?: string // Optional prop
}

const Button: React.FC<ButtonProps> = ({ text, onClick, color }) => {
  return (
    <button style={{ backgroundColor: color }} onClick={onClick}>
      {text}
    </button>
  )
}

In this example, the Button component has three props: text, onClick, and color. The text and onClick props are required, but the color prop is optional.

Now We can skip the color prop here's an example:

<Button text="Click Me" onClick={() => console.log('Button clicked')} />

Children Prop with TypeScript

props.children is a special prop, automatically passed to every component, It allows you to access and render the content that is placed between the opening and closing tags when the component is rendering.

Let's slightly change the above Button component let's change the text prop to children of type ReactNode. in the previous implementation the Button component only limited to simple text values. But now we can pass multiple elements, components.

Here's an updated version of the component:

import React, { ReactNode } from 'react'

type ButtonProps = {
  children: ReactNode
  onClick: () => void
  color?: string // Optional prop
}

const Button: React.FC<ButtonProps> = ({ children, onClick, color }) => {
  return (
    <button style={{ backgroundColor: color }} onClick={onClick}>
      {children}
    </button>
  )
}

export default Button

Now you can use an element or components as a child to indicate the message:

<Button onClick={() => console.log('Button clicked')}>
  <span>Click me</span>
</Button>
<Button onClick={() => console.log('Button clicked')}>
  <RiArrowRightSLine /> {* Icon component   *}
</Button>

Enforced Return Type with TypeScript

TypeScript allows us to enforce the return type of our function components by specifying the type after the colon in the component definition.

const MyComponent: React.FC<MyComponentProps> = ({ name, age }): JSX.Element => {
  return (
    <div>
      <h1>{name}</h1>
      <p>{age}</p>
    </div>
  )
}

In this example, we explicitly specify that the component will return a JSX element.

Conclusion

Using TypeScript with react hooks provides static typing and helps catch errors early in the development process. After reading this article, I hope you can start building your react component with TypeScript in your React projects. Happy coding!

Stay up to date