DEV Community

Cover image for TypeScript with React Components ?
drLacheheb
drLacheheb

Posted on

TypeScript with React Components ?

In this post, I'm going to discuss why and how to use TypeScript to type React components.

You'll find how to annotate component props, mark a prop optional, and indicate the return type.

1. Why typing React components?

TypeScript is useful if you're coding middle and bigger size web applications. Annotating variables, objects, and functions creates contracts between different parts of your application.

For example, let's say I am the author of a component that displays a formatted date on the screen.

interface FormatDateProps {
  date: Date
}

function FormatDate({ date }: FormatDateProps): JSX.Element {
  return <div>{date.toLocaleString()}</div>;
}
Enter fullscreen mode Exit fullscreen mode

According to the FormatDateProps interface, the component FormatDate accepts the date prop as an instance of Date. This constraint is crucial because the FormatDate component uses the date.toLocaleString() method, which only works with date instances.

Now, the user of the FormatDate component must satisfy this constraint:

<FormatDate date={new Date()} />
Enter fullscreen mode Exit fullscreen mode

If the user passes a string to date instead:

<FormatDate date="Sep 28 2021" />
Enter fullscreen mode Exit fullscreen mode

TypeScript will show a type error, ensuring you catch this issue during development.

2. Typing props

One of the best benefits React gains from TypeScript is prop typing.

Typing a React component is typically a 2-step process:

A) Define the interface that describes the component props.

B) Annotate the props parameter in the functional component.

For example:

interface MessageProps {
  text: string;
  important: boolean;
}

function Message({ text, important }: MessageProps) {
  return (
    <div>
      {important ? 'Important message: ' : 'Regular message: '}
      {text}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now, when rendering Message, props must match the defined types:

<Message text="The form has been submitted!" important={false} />
Enter fullscreen mode Exit fullscreen mode

2.1 Props validation

If you provide incorrect props, TypeScript will show a type error during development:

<Message text="The form has been submitted!" important={0} />
Enter fullscreen mode Exit fullscreen mode

2.2 children prop

The children prop holds the content between the opening and closing tags of a component:

<Component>children</Component>
Enter fullscreen mode Exit fullscreen mode

To type the children prop:

interface MessageProps {
  children: JSX.Element | JSX.Element[];
  important: boolean;
}

function Message({ children, important }: MessageProps) {
  return (
    <div>
      {important ? 'Important message: ' : 'Regular message: '}
      {children}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

You can now pass children like so:

<Message important={false}>
  <span>The form has been submitted!</span>
</Message>
Enter fullscreen mode Exit fullscreen mode

or with multiple elements:

<Message important={false}>
  <span>The form has been submitted!</span>
  <span>Your request will be processed.</span>
</Message>
Enter fullscreen mode Exit fullscreen mode

Challenge: How would you update the MessageProps interface to also support a string value as a child?

2.3 Optional props

To make a prop optional, use ?:

interface MessageProps {
  children: JSX.Element | JSX.Element[];
  important?: boolean;
}

function Message({ children, important = false }: MessageProps) {
  return (
    <div>
      {important ? 'Important message: ' : 'Regular message: '}
      {children}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now you can skip the important prop:

<Message>
  <span>The form has been submitted!</span>
</Message>
Enter fullscreen mode Exit fullscreen mode

3. Return type

TypeScript can infer the return type of a React functional component, which is usually JSX.Element:

function Message({ children, important = false }: MessageProps): JSX.Element {
  return (
    <div>
      {important ? 'Important message: ' : 'Regular message: '}
      {children}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

If the component may return null, use JSX.Element | null:

function ShowText({ show, text }: { show: boolean, text: string }): JSX.Element | null {
  return show ? <div>{text}</div> : null;
}
Enter fullscreen mode Exit fullscreen mode

3.1 Tip: enforce the return type

It's recommended to explicitly set the return type:

function BrokenComponent(): JSX.Element {
  return
    <div>Hello!</div>; // Error caught!
}
Enter fullscreen mode Exit fullscreen mode



Feel free to comment below if you have any thoughts or questions!

Top comments (8)

Collapse
 
drlacheheb profile image
drLacheheb • Edited

btw you can do something like this

const Component = (): JSX.Element => {
  return (
    <div>Hello!</div>
  )
};
Enter fullscreen mode Exit fullscreen mode

Or

interface QrText {
    text: string
}

const QrText: React.FC<QrText> = ({ text }) => {
    return (
        <div className="text-center text-l text-[#68778D] font-outfit">
            {text}
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
cookiemonsterdev profile image
Mykhailo Toporkov 🇺🇦

The problem with React.FC is that it is impossible to use generic types like:

type SelectProps<T> = {
  options: T[];
};

const Select = <T extends string | number>({ options}: SelectProps<T>) => {

  return (
      {options.map(option => (
        <div key={option} value={option}>
          {option}
        </div >
      ))}
  );
};

Enter fullscreen mode Exit fullscreen mode
Collapse
 
drlacheheb profile image
drLacheheb

but i like the arrow functions though , that will make me sad hahaha

Collapse
 
alt_exist profile image
Alternate Existance

cool

Collapse
 
martinbaun profile image
Martin Baun

Storybook's a great resource for this!

Collapse
 
drlacheheb profile image
drLacheheb

question did you try to make a markdown editor before ?

Collapse
 
alt_exist profile image
Alternate Existance

Nice post :D

Some comments may only be visible to logged-in visitors. Sign in to view all comments.