When to use JSX.Element vs. ReactNode vs. ReactElement?

Component Type Definition

Many people writing React applications in TypeScript may be confused by the three different function return value types, the differences between them, and when to be more careful about which type to use.

ReactElement is an object with props and type attributes:

type Key = string | number

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

A ReactNode is a collection of many types:

type ReactText = string | number;
type ReactChild = ReactElement | ReactText;

interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
Copy the code

The render member of the class component returns a value of type ReactNode, and the children type specified in PropsWithChildren is also a ReactNode.

const Comp: FunctionComponent = props= > <div>{props.children}</div> 
// children? : React.ReactNode

typePropsWithChildren<P> = P & { children? : ReactNode; }Copy the code

While the React type definition may seem complicated to write, it is essentially equivalent to:

type ReactNode = {} | null | undefined;
Copy the code

Since {} is a prototype for all objects, you can assign almost any type to ReactNode, but in most cases it should be typed in more detail.

Jsx. Element is obtained by executing React. CreateElement or by translating JSX.

const jsx = <div>hello</div>
const ele = React.createElement("div".null."hello");
Copy the code
<p> // <- ReactElement = JSX.Element
  <Custom> // <- ReactElement = JSX.Element
    {true && "test"} // <- ReactNode
  </Custom>
</p>
Copy the code

JSX is a global namespace. Different libraries can implement JSX differently. React implements jsx. Element equivalent to ReactElement, and sets its generic props and type to any:

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

Different return types

Some of you may notice that the return value type of a class component rendering method is different from that of a function component. This is because current versions of TypeScript type definitions don’t exactly define the range of React values:

  • Class component type definition: Returns a ReactNode with Render (), which is looser than the actual value range of React
  • Function component type definition: returns jsX. Element, which is also looser than the actual value range of React

The render() and function components in the React class actually have the same return type, whereas TypeScript simply declares different return types for different types of components for historical reasons and backward compatibility.

According to the documentation, we can give the exact type definition for the component return value:

type ComponentReturnType = ReactElement | Array<ComponentReturnType> | string | number | boolean | null
// Note: undefined cannot be passed
Copy the code