Is there a way to extract the type of the props of a JSX Element?

Viewed 704

My intent is to extract the props out of a given JSX element, is it possible?

This was pretty much my failed attempt... Thanks in advance for any help ;)

function getComponentProps<T extends React.ReactElement>(element: T): ExtractProps<T>;

function Component({ name }: { name: string }) {
  return <h1>{name}</h1>;
}

type ExtractProps<TComponentOrTProps> = TComponentOrTProps extends React.ComponentType<infer TProps>
  ? TProps
  : TComponentOrTProps;

const componentProps = getComponentProps(<Component name="jon" />); //type is JSX.Element
3 Answers

For the most part, you can't do this.

In theory, the React.ReactElement type is generic with a type parameter P that depends on the props. So if you were to have a strongly-typed element then you could work backwards.

type ElementProps<T extends React.ReactNode> = T extends React.ReactElement<infer P> ? P : never;

In reality, you will only get a correct props type if you create your element through React.createElement rather than JSX.

Any JSX element <Component name="John" /> just gets the type JSX.Element which obviously has no information about the props so you cannot work backwards from that to a props type.

const e1 = React.createElement(
  Component,
  { name: 'John' }
)

type P1 = ElementProps<typeof e1> // type: {name: string}

console.log(getElementProps(e1)); // will log {name: "John"}
const e2 = <Component name="John" />

type P2 = ElementProps<typeof e2>  // type: any

console.log(getElementProps(e2)); // will log {name: "John"}

Playground Link

It is much easier to approach the situation from a different angle. You will be able to derive the correct props type if your function takes a component like Component or div rather than a resolved element. You can use the ComponentProps utility type for function and class components and the JSX.IntrinsicElements map for built-in ones.

You can extract type of the Props for any component using

React.ComponentProps<typeof T>

You can refer this TS Playground for more options

import * as React from 'react';

type TProps = {
   name:string;
   age:number;
   isStackoverflow:boolean;
 }

const App = (props:TProps) => <div>Hello World</div>;

//You can extract any components props like this.
type AppProps = React.ComponentProps<typeof App>;

`

This is not possible for getComponentProps(<Component name="jon" />);, since written out JSX-Elements always result in the JSX.Element-type which doesn't give any additional type information which you could extract. It would be possible if you extract it from the component function itself:

export function Component({ name }: { name: string}) {
    return <h1>{name}</h1>;
}

function getComponentProps<T extends (...args: any[]) => JSX.Element>(element: T): Parameters<T>[0] {
    return null as any;
}

const test = getComponentProps(Component); // { name: string;}

This solution uses the utility type parameter, which infers all arguments from a function. We then index the first argument since the prop object is the first argument of a pure jsx function. Class components would need a different solution, though.

Related