22

I have a function that looks at the provided children and if a particular element type is found, it adds some properties to it automatically.

The function is called like this:

render () {

    const { children, name, className } = this.props;

    return (
        <div className={className}>
            {this.enrichRadioElements(children, name)}
        </div>
    )
}

and it is implemented like this:

enrichRadioElements = (children: Array<any>, name: string) => (
    React.Children.map(children, child => {
        if (!React.isValidElement(child)) {
            return child;
        }

        //@ts-ignore
        if (child.props.children) {
            child = React.cloneElement(child, {
                //@ts-ignore
                children: this.enrichRadioElements(child.props.children, name)
            });
        }

        if (child.type === Radio) {
            return React.cloneElement(child, { 
                onChange: this.handleFieldChange,
                selectedValue: this.state.selectedValue,
                name: name
            })
        }
        else {
            return child;
        }
    })
)

The two //@ts-ignore comments are what I'm trying to get rid of by writing code that will satisfy typescript. If I remove the first one, the error message I see is this:

Property 'children' does not exist on type '{}'.(ts-2339)

How can I properly modify my code so I can remove the //@ts-ignore comments? I did go to the definition of child.props and I found this:

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

which looks to have a 'props' of type any (if I'm reading it correctly), but typescript doesn't recognize the children property.

1 Answer 1

30

The problem is a couple of things. I started by changing children: Array<any> to children: React.ReactNode. You already have a check in there to narrow the type from ReactNode to ReactElement. The trick was 1. using the generic type arguments in isValidElement and 2. using a new variable with a type assignment on it elementChild rather than dealing with and mutating the child argument. EnrichedChildren may need to be updated to match your use case.

interface EnrichedChildren {
  onChange(): void
  selectedValue: string
  name: string
  children?: React.ReactNode
}

enrichRadioElements = (children: React.ReactNode, name: string): any =>
  React.Children.map(children, child => {
    if (!React.isValidElement<EnrichedChildren>(child)) {
      return child
    }

    let elementChild: React.ReactElement<EnrichedChildren> = child
    if (child.props.children) {
      elementChild = React.cloneElement<EnrichedChildren>(elementChild, {
        children: this.enrichRadioElements(elementChild.props.children, name),
      })
    }

    if (elementChild.type === 'Radio') {
      return React.cloneElement(elementChild, {
        onChange: () => {},
        selectedValue: 'value',
        name: name,
      })
    } else {
      return elementChild
    }
  })
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.