Can Poyrazoğlu
Can Poyrazoğlu

Reputation: 34780

How to access React child element props in a type safe manner with TypeScript?

I'm trying to access props of a React [Native] component in my app (which are guaranteed to be instance of element of my custom type ItemTemplate:

const children = React.Children.toArray(this.props.children);
return children.find(t => t.props.itemKey == key);

However, in the second line when I try to access t.props I'm getting:

Property 'props' does not exist on type 'ReactElement<ItemTemplate, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | ... 13 more ... | (ReactElement<...>[] & ReactPortal)'.
  Property 'props' does not exist on type 'ReactElement<ItemTemplate, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)>[] & string'.

The code does work correctly, but TypeScript (3.6.3) IntelliSense is complaining. Why?

Upvotes: 3

Views: 1862

Answers (2)

In this case you need to declare or cast the children to ReactElement. Here I have an example of Link component from nextjs to a custom NavLink:

import { ReactElement, cloneElement } from 'react';
import Link, { LinkProps } from 'next/link';

type NavigationData = {
    currentPath: string;
    pathsToMatch: string[] | string;
    activeClassName: string;
    children: ReactElement;
};

type NavLinkProps = LinkProps & NavigationData;

export default function NavLink({
    currentPath,
    pathsToMatch,
    activeClassName,
    children,
    ...props
}: NavLinkProps): ReactElement {
    function GetActiveLink(
        currentPath: string,
        paths: string | string[],
        activeClassName: string
    ): string {
        if (currentPath === '/' && paths === '/') {
            return activeClassName;
        } else if (currentPath !== '/' && paths.indexOf(currentPath) !== -1) 
        {
            return activeClassName;
        }

        return '';
    }

   let className: string = children.props.className || '';
   className += ` ${GetActiveLink(currentPath, pathsToMatch, activeClassName)}`;

    return <Link {...props}>{cloneElement(children, { className })}</Link>;
}

In this way you can access the property prop without any warning.

Happy code. :-)

Upvotes: 2

Prabhunath Yadav
Prabhunath Yadav

Reputation: 185

Use like below:

const children: Array<ReactChild> = React.Children.toArray(this.props.children);
return children.find(t => t.props.itemKey == key);

or with your custom type ItemTemplate:

const children: Array<ItemTemplate> = React.Children.toArray(this.props.children);
return children.find(t => t.props.itemKey == key);

Upvotes: 0

Related Questions