reactjs - Hur man definierar TypeScript för React.Children.map


Jag har en funktion som tittar på de angivna barnen och om en viss elementtyp hittas, lägger den automatiskt till några egenskaper till den.

Funktionen kallas på detta sätt:

render () {

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

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

och det implementeras så här:

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;
        }
    })
)

De två //@ts-ignore-kommentarerna är det jag försöker bli av med genom att skriva kod som kommer att tillfredsställa TypeScript. Om jag tar bort den första, är felmeddelandet jag ser detta:

Egenskapen 'children' finns inte på typen '{}'.(ts-2339)

Hur kan jag på ett korrekt sätt ändra min kod så att jag kan ta bort //@ts-ignore-kommentarerna? Jag gick till definitionen av child.props och jag hittade detta:

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

som verkar ha en 'props' av typen any (om jag tolkar det rätt), men TypeScript känner inte igen children-egenskapen.

Svar #1

Problemet är ett par saker. Jag började med att ändra children: Array<any> till children: React.ReactNode. Du har redan en kontroll där för att begränsa typen från ReactNode till ReactElement. Tricket var 1. att använda de generiska typeargumenten i isValidElement och 2. att använda en ny variabel med en typdeklaration på elementChild istället för att hantera och ändra child-argumentet. EnrichedChildren kan behöva uppdateras för att passa ditt användningsfall.

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
    }
  })

Relaterade artiklar