Home > other >  Accessing child.type.name in React Typescript
Accessing child.type.name in React Typescript

Time:11-09

I'm using React Functional components throughout a Typescript application.

I'm trying to dynamically update some child components in another component, as below:

const memoizedIterateAndAddProps = useCallback(
    (kids: ReactNode) => {
      return React.Children.map(kids, (child) => {
        if (React.isValidElement(child)){
           console.log(child.type.name)
          }
      }
  }, [])

but typescript keeps complaining with Property 'name' does not exist on type 'string | JSXElementConstructor<any>'. Property 'name' does not exist on type 'string'.ts(2339)

I get this is related to the fact that a child could just be a string, but I can't figure out a way of solving this that makes Typescript happy.

I'd be incredibly grateful for any help from anyone

CodePudding user response:

React.isValidElement has this type signature:

function isValidElement<P>(object: {} | null | undefined): object is ReactElement<P>;

Above typeguard, narrows child to be ReactElement which has this type signature:

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

As you might have noticed, type is a union of string and JSXElementConstructor<any>.

Hence, if you want to make sure that you have a deal with function, you should use custom typeguard:

const isFunction=(data:any):data is (...args:any[])=>any => typeof data === 'function'

WHole code:

import React, { useCallback, ReactNode } from 'react'
const isFunction = (data: any): data is (...args: any[]) => any => typeof data === 'function'

const App = () => {
  const memoizedIterateAndAddProps = useCallback(
    (kids: ReactNode) => {
      return React.Children.map(kids, (child) => {
        if (React.isValidElement(child) && isFunction(child.type)) {
          console.log(child.type.name)
        }
      }
  }, [])

  return null
}

Playground

CodePudding user response:

...but I can't figure out a way of solving this that makes Typescript happy.

Check that it isn't a string:

const memoizedIterateAndAddProps = useCallback(
    (kids: ReactNode) => {
        return React.Children.map(kids, (child) => {
            if (React.isValidElement(child) && typeof child.type !== "string") {
//                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               console.log(child.type.name);
            }
        });
    }
);

(Or && "name" in child.type would work too.)

You get a string for type when you do <div/> and such, so you have to filter out that case if you want to use name.

  • Related