chevron-left chevron-right

Typing functions with TypeScript. How to do it properly in ReactJS?

When I started learning Typescript with React I was able to stick with the convention of defining components with a const keyword. After some I switched teams and we decided to use function declarations as a convention for defining React components.

And then the problem appeared to me. While using arrow functions it was pretty straightforward to start typing, with TypeScript, components in React, the way of typing function declarations was completely different to me and unknown.

Function expressions vs function declarations. What is the difference?

At first glance, there are no difference on how both types of functions work in general. The visible difference is a different annotation.

Function expression

const getTimestamp = () => {

Function declaration

function getTimestamp() {

Other than that there are practically no meaningful differences now, except hoisting. It's just a convention you or your team picks in order to keep codebase consistent.

Typing components as function expressions in TypeScript

Let's take a look at typing function expressions. It's the most common approach, when developing React code.

import * as React from 'react';

interface Props {
    title: string;

const SomeComponent: React.FC<Props> = ({title}) => {
    return <div>SomeComponent body</div>;

As you can see it's pretty straightforward. You're defining:

  1. Component props types with an interface keyword (you might also use type if you prefer).
  2. Next, you're declaring that SomeComponent has the type of React's FunctionComponent.
  3. You don't need to type the function's output as it's already defined by usage of React.FC.

This way you could type your component properly, which is just enough for simple use cases.

Typing components as function declarations in TypeScript

import * as React from 'react';

interface Props {
    title: string;

function SomeComponent({title}: Props): JSX.Element => {
    return <div>SomeComponent body</div>;

You might have noticed, that notation is different. You're not using React.FC definition anymore and you have to type the output, hence TypeScript will know the type of your components when using it somewhere in a codebase. I have typed the output as of JSX.Element type. I'm explaining why in the next section.

What's the difference between JSX.Element, ReactNode and ReactElement?

In this section I'm going to explain you the difference between JSX.Element, ReactNode and ReactElement.

TLDR; in most cases you'll want to use JSX.Element type as function's output type.

JSX.Element and ReactElement

They're almost the same, but with slight difference. While ReactElement is an object with a type and props, the JSX.Element is a ReactElement with the generic type for props and type being any.

Here's the detailed definition for ReactElement:

interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
    type: T;
    props: P;
    key: Key | null;

And the definition of JSX.Element:

declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> { }

As you can see JSX is a global namespace that gets definition of Element from ReactElement. Each library can define it differently. In React, it's defined like above.


On the other hand, ReactNode is less strict than the two above. It represents the following: {} | undefined | null and that's the reason it's used as a type of React children. You don't know whether children exist or not and what is their actual type.


With the knowledge presented in the article you'll be able to developer error-proof web apps with ease. I hope that now you get how to type function expressions and function declarations properly in React and understand the difference between JSX.Element, ReactElement and ReactNode. I wanted to share with you that knowledge because I remember the time when I had such doubts how to type React components in a correct manner.